-
设值注入,通过set的方式注入,自动的调用类的set方法给属性赋值
查看全部 -
通过xml的方式将bean加载到容器,通过xml来获取bean
查看全部 -
类自动检测及Bean的注册:
<context:component-scan base-package="org.example"/>
查看全部 -
Spring注解:@Configuration,@Bean,@Import,@DependsOn
@Component是一个通用注解,可以作为元注解,可用于任何bean
@Repository -DAO层
@Service - Service层
@controller -Controller层
查看全部 -
Bean的生命周期:定义,初始化,使用,销毁
//初始化 配置init-method <bean id="initBean" class="examples.ExampleBean" init-method="init"></bean> //销毁 配置destroy-method <bean id="destroyBean" class="examples.ExampleBean" destroy-method="cleanup"></bean>
查看全部 -
Spring 注入方式:设值注入,构造注入
查看全部 -
接口:
面向接口编程:
接口是用于隐藏具体实现和实现多态性的组件
接口实现的变动不影响各层间的调用,
风情层次级调用关系,每层只向外提供一组件功能接口,各层仅依赖接口而非实现类
查看全部 -
课程内容:
什么是框架?
Spring简介
IOC配置注解
Bean配置注解
AOP配置注解,AspectJ,API
查看全部 -
ioc:控制反转,控制权的转移,应该是程序本身不负责依赖对象的创建和维护,而是由外部容器负责创建和维护。
di:依赖注入是一种实现方式
查看全部 -
Spring中把所有的对象称为bean
查看全部 -
Spring的Ioc是通过外部容器来创建和维护对象, 而不是程序自己本身创建, 依赖注入是其实现方式, 本质是为了实现降低耦合度的目的.
查看全部 -
旮旯查看全部
-
元注解,@component
查看全部 -
Maven
(1)是一个可以通过一小段描述信息来管理项目的构建、报告和文档的软件项目管理工具
(2)作用:在文件中添加相应的配置,maven就可以自动下载对应的jar包;该jar包依赖的jar包也会被自动下载下来;可以直接通过他打包压缩文件或jar项目
1°下载jar包
Maven项目有一个pom.xml文件,只要添加对应的配置,就可以自动下载jar包。
在代码中,对于一个<dependency>结点,其中有
项目名:<groupId>junit</groupId>
项目模块:<artifactId>junit</artifactId>
项目版本:<version>4.8.2</version>
来对应的下载jar包
2°寻找和下载依赖的jar包
3°热部署,热依赖
在web项目已经运行时,修改代码可以直接被web服务器接受,不需要重启服务器或重新部署代码;可以直接通过maven打包war或jar项目
pom.xml解析
xsi全名:xml schema instance 即xml架构实例
xmlns是web.xml文件用到的命名空间
xmlns:xsi是指web.xml遵守xml规范
xsi:schemaLocation是指具体用到的schema资源
properties指常量,在pom的其它地方可以直接引用
dependencies 依赖关系
build构建配置
plugins插件列表
depositories仓库配置
distributionManagement 分发配置
profile“用户配置文件”是针对每个帐户的数据存储
Nginx
Nginx同Apache一样都是一种WEB服务器。基于REST架构风格,以统一资源描述符(Uniform Resources Identifier, URI)或者统一资源定位符(Uniform Resources Locator, URL)作为沟通依据,通过HTTP协议提供各种网络服务。
Apache是重量级的、不支持高并发、性能差,作为替代品,Nginx应运而生。
Nginx是一款自由的、开源的、高性能的HTTP服务器和反向代理服务器;同时也是一个IMAP、POP3、SMTP代理服务器;Nginx可以作为一个HTTP服务器进行网站的发布处理,另外Nginx可以作为反向代理进行负载均衡的实现。
JavaBean
1.JavaBean是使用Java语言开发的一个可重用的组件,在开发中可以使用JavaBean减少重复代码,使整个代码的开发更简洁
2.一个类中只包含属性、setter、getter方法,那么这种类就成为简单JavaBean。
简单JavaBean的名词:
(1)VO:与简单Java对象对应,专门用于传递值的操作上
(2)POJO:简单Java对象
(3)TO:传输对象,进行远程传输时,对象所在的类必须实现java.io.Serializable接口。
3.Web项目中的目录:
WEB-INF:Web目录中最安全的文件夹,保存各种类、第三方jar包、配置文件
Web.xml:Web的部署描述符
4.xml文件,表示可扩展标记语言,是一种很像HTML的标记语言,旨在传输数据而不是显示数据。
5.web.xml
web.xml文件并不是web工程必须的。
web.xml文件是用来初始化配置信息:比如Welcome页面、servlet、servlet-mapping、filter、listener、启动加载级别等。
JSON
JSON是一种取代XML的数据结构,和xml相比,它更小巧但描述能力却不差,由于它的小巧所以网络传输数据将减少更多流量从而加快速度,
那么,JSON到底是什么?
JSON就是一串字符串 只不过元素会使用特定的符号标注。
{} 双括号表示对象
[] 中括号表示数组
"" 双引号内是属性或值
: 冒号表示后者是前者的值(这个值可以是字符串、数字、也可以是另一个数组或对象)
所以 {"name": "Michael"} 可以理解为是一个包含name为Michael的对象
而[{"name": "Michael"},{"name": "Jerry"}]就表示包含两个对象的数组
当然了,你也可以使用{"name":["Michael","Jerry"]}来简化上面一部,这是一个拥有一个name数组的对象
GSON
GSON是Google提供的用来在Java对象和JSON数据之间进行映射的Java类库。可以将一个Json字符转成一个Java对象,或者将一个Java转化为Json字符串。
GSON的创建方式:
(1)Gson gson = new gson();
(2)通过GsonBuilder()配置多种配置
Gson gson = new GsonBuilder().配置方法().create();
方法:
public String toJson(Object obj):将对象转换为JSON字符串
public T fromJson(String jsonStr, T.class):将JSON字符串转换为java对象
servlet
实质就是运行在 Web 应用服务器上的 Java 程序,与普通 Java 程序不同,它是位于 Web 服务器内部的服务器端的 Java 应用程序,可以对 Web 浏览器或其他 HTTP 客户端程序发送的请求进行处理。
Servlet的框架是由两个Java包组成:javax.servlet和javax.servlet.http. 在javax.servlet包中定义了所有的Servlet类都必须实现或扩展的的通用接口和类.在javax.servlet.http包中定义了采用HTTP通信协议的HttpServlet类.
广义的servlet指实现了Servlet接口的类,一般情况下Servlet用来拓展基于HTTP协议的Web服务器。编写一个 Servlet 其实就是按照 Servlet 规范编写一个 Java 类。
Servlet没有main方法,它的对象的运行需要Servlet容器(Web容器)的支持,它的创建、使用、销毁都由Servlet容器进行管理(如Tomcat)。Servlet被部署到Servlet容器中,由容器来实例化和调方法。
有了 Servlet 之后,用户通过单击某个链接或者直接在浏览器的地址栏中输入 URL 来访问 Servlet ,Web 服务器接收到该请求后,并不是将请求直接交给 Servlet ,而是交给 Servlet 容器。Servlet 容器实例化 Servlet ,调用 Servlet 的一个特定方法对请求进行处理, 并产生一个响应。这个响应由 Servlet 容器返回给 Web 服务器,Web 服务器包装这个响应,以 HTTP 响应的形式发送给 Web 浏览器。
Servlet是和HTTP协议紧密联系的,它可以处理HTTP协议相关的所有内容。这也是Servlet应用广泛的原因之一
Servlet的主要作用功能:接受从浏览器发送过来的HTTP请求(request),并返回HTTP响应(response)。这个工作是在service方法中完成的。要获取客户端提交的数据需通过request,要想容器输出数据需通过response。
Servlet工作原理解析:一个HTTP请求的执行过程:
客户端发出请求http://localhost:8080/xxx
根据Web.xml文件的配置,找到<url-pattern>对应的<servlet-mapping>
读取<servlet-mapping>中<servlet-name>的值
找到<servlet-name>对应的<servlet-class>
找到该class并加载执行该class
在Web容器中,Servlet主要经历4个阶段:
(1)加载Servlet
(2)初始化Servlet init()方法
(3)处理请求service()方法
(4)处理完成destory()方法
开发Servlet程序:
(a)定义一个类HelloServlet,并让该类去实现javax.servlet.Servlet接口;
(b)实现Servlet接口中的init,service,destory等方法.
配置 Servlet:
上面编写好的 HelloServlet 类仅仅是一个普通的实现类而已,而现在我想要它运行在我自己的 Tomcat 服务器中,所以应该通知 Tomcat 服务器来管理我的 HelloServlet 类
Servlet 提供处理请求的方法:
Servlet 即实现了 Servlet 接口的类,当我们创建一个自定义类,实现 Servlet 接口 的时候,会发现有 5 个方法需要重写,有init【初始化】,destroy【销毁】,service【服务】,ServletConfig【Servlet配置】,getServletInfo【Serlvet信息】。
这样做的话,我们每次都需要实现 5 个方法,太麻烦了!
我们可以直接继承 HttpServlet 类,该类已经默认实现了 Servlet 接口中的所有方法,在编写 Servlet 的时候,你只需要重写你需要的方法就好了。
对于每次访问请求,Servlet引擎都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调用的Servlet的service()方法,service方法再根据请求方式分别调用doXXX方法。
GET 和 POST 的区别:
要知道,GET 和 POST 都是请求方式
GET:
浏览器器地址栏:http://localhost/test.html?name=wmyskxz&sex=male
这里提交了两个参数,一个是name属性值为wmyskxz,另一个是sex属性值为male,这是一种直接的请求方式,在请求资源后面跟上 ? 符号与参数连接,其他的参数使用 & 符号连接。
缺点:
1.暴露请求信息,不安全
2.请求信息不能超过1kb,可传输的信息有限,不能上传图片
POST:
浏览器地址栏:http://localhost/test.html#
优点:
1.隐藏了请求信息,较安全(但仍可以通过相关工具访问到数据)
2.POST 方式没有限制请求的数据大小,可以做图片的上传
但并不是所有的数据都需要使用 POST 请求来完成,事实上,GET 请求方式会比 POST 请求更快,当数据小并且安全性要求不是那么高的时候,GET 仍然是很好的选择.(并且 GET 相较 POST 简单)
MVC 模式
MVC 是一种分层的设计模式 。
M 代表 模型(Model)
模型是什么呢? 模型就是数据,就是dao,bean
V 代表 视图(View)
视图是什么呢? 就是网页, JSP,用来展示模型中的数据
C 代表 控制器(controller)
控制器是什么? 控制器的作用就是把不同的数据(Model),显示在不同的视图(View)上。
HTTPServlet
当Web容器接收到某个Servlet请求时,Servlet把请求封装成一个HttpServletRequest对象,然后把对象传给Servlet的对应的服务方法.
HTTP的请求方式包括DELETE(删),GET(查),OPTIONS,POST(增),PUT(改)和TRACE,在HttpServlet类中分别提供了相应的服务方法DoGet()等
HttpServlet的功能:读取Http请求的内容。
请求流程如下:
1)Web客户向Servlet容器发出Http请求;
2)Servlet容器解析Web客户的Http请求;
3)Servlet容器创建一个HttpRequest对象,在这个对象中封装Http请求信息;
4)Servlet容器创建一个HttpResponse对象;
5)Servlet容器调用HttpServlet的service方法,把HttpRequest和HttpResponse对象作为service方法的参数传给HttpServlet对象;
6)HttpServlet调用HttpRequest的有关方法,获取HTTP请求信息;
7)HttpServlet调用HttpResponse的有关方法,生成响应数据;
8)Servlet容器把HttpServlet的响应结果传给Web客户。
Tomcat
(1)轻量级Web应用服务器(是一个Servlet/JSP容器,Servlet/JSP程序需要运行在web容器上)。通俗来讲,万维网的本质是超文本文档(HTML文档)组成的一个通过超级链接互相访问交互网络,当甲计算机上的文档A通过超链接访问乙计算器上的文档B,B必须放在Web服务器才能被访问
(2)应用程序是一个WAR(WebArchive)文件,根目录下有Html和JSP文件或包含这两种文件的目录,还有一个WEB-INF目录。WEB-INF目录下有一个web.xml文件和一个classes目录,web.xml是这个应用的配置文件,classes目录下则包含编译好的Servlet类和Jsp或Servlet所依赖的其它类(如JavaBean)
(3)还具有传统的Web服务器的功能:处理Html页面
单元测试
1.断言
(1)断言:也就是所谓的assertion,是jdk1.4后加入的新功能。
(2)它主要使用在代码开发和测试时期,用于对某些关键数据的判断,如果这个关键数据不是你程序所预期的数据,程序就提出警告或退出。java使用assert作为一个关键字
(3)语法
语法1:assert expression;
//expression代表一个布尔类型的表达式,如果为真,就继续正常运行,如果为假,程序退出
语法2:assert expression1 : expression2;
//expression1是一个布尔表达式,expression2是一个基本类型或者Object类型,如果expression1为真,则程序忽略expression2继续运行;如果expression1为假,则运行expression2,然后退出程序。
(4)断言功能是用于软件的开发和测试的,也就是说,删去断言的那部分语句后,你程序的结构和运行不应该有任何改变,千万不要把断言当成程序中的一个功能来使用
2.Junit
(1)是一个Java语言的单元测试框架
(2)为什么要使用Junit:
通常一个项目中有成千上万的方法,以前一般的做法是写一些测试代码看输出结果,然后由自己来判断结果是否正确,使用JUnit的好处就是这个结果是否正确的判断是由JUnit来完成的。我们只要关注结果是否正确就可以了。测试框架可以帮助我们对编写的程序进行有目的性的测试,减少代码中的bug,使用断言机制直接将预期结果与实际结果对比,确保对结果的可预知性。
(3)JUint包含:junit.jar和hamcrest-core.jar:设置匹配性规则的一个框架,可以有效增强JUnit测试的能力。
(4)用IDEA进行单元测试(对src的包下的已经存在的类进行):
在src目录下创建一个新的包test,再创建一个类,右键选中在按SHIFT+CTRL+ALT+S,选择Modules的Dependencies右边的绿色加号,在IDEA的安装目录下找到lib下的两个jar包:Junit-4.12和hamcrest-core-1.3。
然后可以通过@Test注释(需要导包org.junit.Test,将普通方法修饰为测试方法)来添加断言方法,如:
@Test
public void testAdd() {
assertEquals(6, new Caculate().add(4,2));
}
其中assertEquals()方法是org.junit.Assert中的方法,可以用静态导入(import static org.junit.Assert.*),如果期望值和实际值一致则没问题,如果不一致会抛出AssertError错误。
注意所有方法都是public void修饰的
(5)Junit通过@BeforeClass @AfterClass @Before @After可以调用其他方法,其中快捷键ALT+INSERT中出现的setUp Method(@Before)为每个测试方法运行前运行的方法,tearDown Method(@After)为每个测试方法运行后运行的方法
(6)Test有两个属性:@Test(expected=xx.class) ,如@Test(expected = ArithmeticException.class)则当被除数为0是不会抛出算数异常(预期已经抛出该异常)
和@Test(timeout=毫秒) 超过该时间则结束
(7)@Ingore修饰的方法不会被执行
(8)通过测试套件来组织测试类一起运行
@RunWith(Suite.class)
@Suite.SuiteClasses({TaskTest1.class, TaskTest2.class})
public class SuiteTest {
//…
}
该类是一个空类,通过@RunWith(Suite.class)来开始批量测试,用@Suit.SuiteClasses来放入需要进行测试的测试类
(9)参数化设置,通过@RunWith(Parameterized.class)实现
@Runwith(Parameterized.class)
public class ParameterTest {
int expected = 0;
int input1 = 0;
int input2 = 0;
@Parameters
public static Collection<Object[]> t() {
return Arrays.asList(new Object[][] {
{3,1,2},
{4,2,2}
});
}
public ParameterTest(int expected, int input1, int input2) {
this.expected = expected;
this.input1 = input1;
this.input2 = input2;
}
@Test
public void testAdd() {
assertEquals(expected, new Calculate().add(input1,input2));
}
}
先定义存放数据的变量,然后声明一个返回值为Collection的静态方法来存放数据,然后提供一个构造方法,最后写一个测试方法。注意@RunWith()注释中的是Parameterized的class对象
JDBC
JDBC(Java DataBase Connectivity)是Java和数据库的桥梁
编程步骤:
(1)装载数据库的JDBC驱动并进行初始化
1°导入jar包,如mysql-connector-java-5.0.8-bin.jar
2°初始化驱动类com.mysql.jdbc.Driver
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
(需要捕获异常)
(2)建立JDBC和数据库之间的连接
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/exam?characterEncoding=UTF-8", "root", "admin");
包括三个参数(URL,数据库的用户名,数据库的密码)
其中URL包括jdbc:mysql://数据库的主机:端口/要操作的数据库的名称?编码方式
Connection接口必须在用完关闭
(3)获取Statement或PreparedStatement接口,来执行sql语句
Statement容易出错,不常用
增加 删除 修改:
String sql = “update t_course set course_name=? where courese_id=?”;
PreparedStatement ps = c.prepareStatement(sql);
ps.setString(1, “chinese”);
ps.setInt(2, 5);
ps.executeUpdate(); //增删改都使用Update ;可以有返回值,标识多少条数据受到了影响
查询:
String sql = “select * from tb_user where id=?”; //其中?是一个占位符,后面来对它赋值
PreparedStatement ps = c.prepareStatement(sql);
ps.setLong(1,1L); //给第一个占位符赋值为1L
ResultSet rs = ps.executeQuery();
当数据量比较大时,可以使用批量添加
for(int i=1; i<100; i++) {
pstmt.setInt(1, 8000+i);
pstmt.setString(2, "赵_"+i);
pstmt.addBatch();
//批量更新
if(i%10 == 0) {
pstmt.executeBatch();
}
}
(4)执行sql语句,如果有结果,将它返回集合ResultSet,并对结果进行处理
ResultSet rs = ps.executeQuery();
while(resultSet.next()) { //遍历结果
System.out.println(resultSet.getString("Name")); //根据括号中的属性值得到结果
System.out.println(resultSet.getInt("age"));
}
(5)释放资源(从里到外)
if(rs != null) {
try {
rs.close();
}catch (SQLException e) {
e.printStackTrace();
}
}
if(ps != null) {
try {
rs.close();
}catch (SQLException e) {
e.printStackTrace();
}
}
if(c != null) {
try {
rs.close();
}catch (SQLException e) {
e.printStackTrace();
}
}
加密算法
1.哈希算法:哈希算法是单向的,可以将任何大小的数据转化为定长的密文,而且无法被反向计算。另外,即使数据源只改动了一丁点,哈希的结果也会完全不同。这样的特性使得它非常适合用于保存密码。作为哈希函数的一种,MD5(消息摘要算法)被广泛应用,但是它所产生的哈希值非常弱,容易通过字典攻击和暴力攻击破解。
2.密码加盐:盐是在获取密码哈希值过程中添加到密码的一段随机序列,它能够防止通过预先计算的彩虹表破解。每个用户创建属于自己的盐,这样一来,即使用户的密码相同,通过加盐后的哈希值也将不同。在获取盐值后,将用户的原始密码与盐值一起加密得到密文。同时盐值通常和密码哈希值一起存放在账户数据库中,或者直接存为哈希字符串的一部分。当需要验证用户的密码时,需要将盐值取出,按照之前的加密规则与密码一起重新加密,并验证两次的密文是否相同。
3.SHA(安全散列算法)和MD5相似,但是它产生的哈希值很强,如SHA256算法的哈希值大小为256位。
4.PBKDF2加密算法:全称Password-Based Key Derivation Function,属于哈希算法。它可以使哈希函数足够慢以阻止攻击,但仍然足够快以至于不会对用户造成明显的延迟。PBKDF2算法使用SHA256作为伪随机函数,并对密码加盐,同时指定迭代次数来减慢暴力破解的速度,具有很强的安全性。在Java中的实现为PBKDF2WithHmacSHA1。
MyBatis
1.在Java中,对数据库的常用工具是JDBC,它的工作量大,开发者需要先注册数据库驱动、建立数据库连接、创建Statement对象来执行sql语句、设置查询参数、执行查询并将结果集返回ResultSet对象,解析结果集,最后还需要释放资源。通过JDBC来访问数据库,每一次都需要进行上述一些重复的繁杂的代码。
主要缺点如下:
(1) 频繁的对数据库创建和释放连接接会造成系统资源的浪费。
(2) sql语句写在代码里,属于硬编码,不易进行维护。
(3) sql参数硬编码,不易进行维护。
(4) 对结果集的解析麻烦,需要开发者自己去读取并生成对应的pojo对象。
2.MyBatis对JDBC很好地进行了封装,解决了上述缺点,分别如下:
(1) 在SqlMapConfig.xml配置文件中设置数据库连接池,通过数据库连接池来管理数据库。
(2) 将sql语句配置在xxxmapper.xml文件中,与代码分离。
(3) MyBatis自动将java对象映射给sql语句。
(4) MyBatis自动将sql语句的执行结果映射给java对象
3.Mybatis使用的主要步骤(使用传统Dao的开发方式):
(1) 导入依赖的jar包
(2) 配置MyBatis核心配置配置SqlMapConfig.xml,包括:
a)使用properties导入连接数据库的配置信息
b)使用environment来配置JDBC事务管理器、数据库连接池
c)配置映射文件(将sqlSessionFactory注入到实现类)
(3) 配置log4j.properties文件
(4) 创建pojo类
(5) 创建Dao接口
(6) 创建DaoImpl实现类
(7) 创建Mapper映射文件xxx.xml,包括sql语句
(8) 进行单元测试
4.传统Dao开发的方式是在实现类中注入SqlSessionFactory并通过工厂类获取SqlSession对象,缺点是在实现类中存在大量模板方法,并且调用SqlSession方法时存在硬编码;使用Mapper代理的方式进行开发,只需要写dao接口(Mapper),而不需要写dao实现类,由mybatis根据dao接口和映射文件中statement的定义生成接口实现代理对象。
5.Mybatis使用的主要步骤(使用Mapper动态代理的开发方式):
(1) 导入依赖的jar包
(2) 配置MyBatis核心配置配置SqlMapConfig.xml,包括:
a)使用properties导入连接数据库的配置信息
b)使用environment来配置JDBC事务管理器、数据库连接池
c)配置映射文件
i.方法1:通过配置MapperFactoryBean类,将sqlSessionFactory注入到接口
ii.方法2使用包扫描:通过MapperScannerConfigurer类将mapper接口所在的包赋给basePackage(比方法
1更简单)
(3) 配置log4j.properties文件
(4) 创建pojo类
(5) 创建Mapper接口
(6) 创建xxxMapper.xml映射文件,包括sql语句,注意:
a)namespace必须是接口的全路径名
b)接口的方法名必须与sql id 一致
c)接口的入参必须与parameterType类型一致
d)接口的返回值必须与resultType类型一致
(7) 进行单元测试
6.
(1)使用Spring:
Spring用来管理对象关系,可以根据配置文件来创建及组装对象之间的依赖关系,Spring的面向切面编程能帮助我们无耦合地实现日志记录、性能统计、安全控制等。
(2)Spring整合MyBatis步骤:
1°重新配置MaBatis的配置文件SqlMapConfig.xml,不再包含数据库连接、数据库连接池、SqlSessionFactory对象等内容。
2°创建Spring的配置文件applicationContext.xml,将SqlMapConfig.xml中删除的内容交给Spring来完成。
查看全部 -
第五章 Spring AOP基本概念
主要内容:
什么是AOP及实现方式
AOP基本概念
Spring中的AOP
AOP实现方式之Schema-based AOP(配置方式实现)
Spring AOP API
AOP实现方式之AspectJ
5-1 AOP基本概念及特点
1.什么是AOP
(1)AOP:Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
(2)主要的功能是:日志记录、性能统计、安全控制、事务处理、异常处理等
(3)切面,是与功能垂直的,比如日志要对每一个模块的每一个点处都进行记录
2.AOP的实现方式
(1)预编译
如AspectJ
(2)运行期动态代理(JDK动态代理、CGLib动态代理)
如SpringAOP、JbossAOP
3.AOP相关概念
(1)切面Aspect:一个关注点的模块化,这个关注点可能会横切(控制)多个对象
(2)连接点Joinpoint:程序执行过程中的某个特定的点
(3)通知Advice:在切面的某个特定的连接点上(额外)执行的动作
(4)切入点Pointcut:匹配连接点的断言,在AOP中通知和一个切入点表达式关联
(5)引入Introduction:在不修改类代码的前提下,为类添加新的方法和属性(类似于编译器将java文件编程为class文件)
(6)目标对象Target Object:被一个或多个切面通知的对象
(7)AOP代理AOP Proxy:AOP框架创建的对象,用来实现奇恩契约(aspect contract)(包括通知方法执行等功能)
(8)织入Weaving:把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象,分为:编译时织入、类加载时织入、执行时织入
4.通知Advice的类型
(1)前置通知Before advice:在某连接点(join point)之前执行的通知,但不能阻止连接点前的执行(除非它抛出一个异常)
(2)返回后通知After returning advice:在某连接点(join point)正常完成后执行的通知
(3)返回异常后通知After throwing advice:在方法抛出异常退出时执行的通知
(4)后通知After(finally) advice:当某连接点退出时执行的通知(不论是正常返回还是异常退出)
(5)环绕通知Around Advice:包围一个连接点(join point)的通知
5.Spring框架中AOP的用途
(1)提供了声明式的企业服务,特别是EJB的替代服务的声明
(2)允许用户定制自己的切面,以完成OOP与AOP的互补使用
6.Spring的AOP实现
(1)纯java实现,无需特殊的编译过程,不需要控制类加载器层次
(2)目前只支持方法执行连接点(通知Spring Bean的方法执行)
(3)不是为了提供最完整的AOP实现(尽管它非常强大);而是侧重于提供一种AOP实现和Spring IoC容器之间的整合,用于帮助解决企业应用中的常见问题
(4)Spring AOP不会与AspectJ竞争,从而提供综合全面的AOP解决方案
7.有接口和无接口的Spring AOP实现的区别
(1)有接口:Spring AOP默认使用标准的JavaSE动态代理作为AOP代理,这使得任何接口(或接口集)都可以被代理
(2)无接口:Spring AOP中也可以使用CGLIB代理(如果一个业务对象并没有实现一个接口)
下面内容:Schema——基于配置的AOP实现
5-2 配置切面aspect
1.Spring所有的切面和通知器都必须放在一个<aop:config>内(可以配置包含多个<aop:config>元素),每一个<aop:config>可以包含pointcut,advisor和aspect元素(它们必须按照这个顺序进行声明)
2.<aop:config>风格的配置大量使用了Spring的自动代理机制
3.声明aspect要用到<aop:config>和<aop:aspect>
<aop:config>
<aop:aspect> id="myAspect" ref="aBean">
…
<aop:aspect>
<aop:config>
<bean id="aBean" class="…">
…
</bean>
5-3 配置切入点Pointcut
1.声明
execution(public * *(..)):切入点为执行所有public方法时
execution(* set*(..)):切入点为执行所有set开始的方法
execution(* com.xyz.service.AccountService.*(..)):切入点为执行AccountService类中的所有方法时
execution(* com.xyz.service..(..)):切入点为执行com.xyz.service包下的所有方法时
execution(* com.xyz.service…(..)):切入点为执行com.xyz.service包及其子包下的所有方法时
with(com.xyz.service.*)(only in Spring AOP)
with(com.xyz.service..*)(only in Spring AOP) within用于匹配指定类型内的方法执行
this(com.xyz.service.AccountService) (only in Spring AOP) this用于匹配当前AOP代理对象类型的执行方法
等等很多,用的时候再查
2.例子
1°
<aop:config>
<aop:pointcut id="businessService"
//在执行service包下的所有类的任何类型的方法时,引入切入点
expression="execution(* com.xyz.myapp.service..(..))"/>
</aop:config>
2°
<aop:config>
<aop:pointcut id="businessService"
//在执行该businessService方法时,引入切入点
expression="com.xyz.myapp.SystemArchitecture.businessService()"/>
</aop:config>
3.完整的配置
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service..(..))"/>
…
</aop:aspect>
<aop:config>
5-4 Advice应用(配置)(上)
1.前置通知Before advice
声明方式:
1°
<aop:aspect id="beforeExample" ref="aBean">
<aop:before
pointcut-ref="dataAccessOperation" //声明参考切入点dataAccessOperation的声明
method="doAccessCheck"/> //调用aBean的doAccessCheck方法
…
</aop:aspect>
2°
<aop:aspect id="beforeExample" ref="aBean">
<aop:before
pointcut ="execution(* com.xyz.myapp.dao..(..))" //直接进行声明
method="doAccessCheck"/>
…
</aop:aspect>
2.返回后通知After returning advice
声明方式:
<aop:aspect id="afterReturningExample" ref="aBean">
<aop: after-returning
pointcut-ref ="dataAccessOperation"
//returning="retVal" //可以使用returning属性来限制返回值
method="doAccessCheck"/>
…
</aop:aspect>
3.返回异常后通知After throwing advice
声明方式:
<aop:aspect id="afterThrowingExample" ref="aBean">
<aop: after-throwing
pointcut-ref ="dataAccessOperation"
//throwing="dataAccessEx" //可以使用throwing属性来指定可被传递的异常的参数名称
method="doRecoverActions"/>
…
</aop:aspect>
4.后通知After(finally) advice
声明方式:
<aop:aspect id="afterFinallyExample" ref="aBean">
<aop: after
pointcut-ref ="dataAccessOperation"
method="doReleaseLock"/>
…
</aop:aspect>
5-5 Advice应用(配置)(下)
1.环绕通知Around Advice
(1)通知方法的第一个参数必须是ProceedingJoinPoint类型
(2)声明方式
<aop:aspect id="aroundExample" ref="aBean">
<aop: around
pointcut-ref ="businessService"
method="doBasicProfiling"/>
…
</aop:aspect>
(3)使用方式
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
//执行业务方法前,可以有方法
Object retVal = pjp.proceed(); //执行业务方法
//执行业务方法后,可以有方法
return retVal;
}
2.advice的参数
通过在xml配置,可以将实现类的参数传给环绕通知方法的参数,进而来使用
接口:
public interface FooService {
Foo getFoo(String fooName, int age);
}
实现类:
public class DefaultFooService implements FooService {
public Foo getFoo(String name, int age) {
return new Foo(name, age);
}
}
配置:
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<bean id="profiler" class="x.y.SimpleProfiler"/>
<aop:config>
<aop:aspect ref="profiler">
<aop:pointcut id="theExecutionOfSomeFooServiceMethod"
expression="execution(* x.y.service.FooService.getFoo(String, int)) and args(name, age)" />
<aop:around pointcut-ref=" theExecutionOfSomeFooServiceMethod"
method="profile"/>
</aop:aspect>
<aop:config>
环绕通知:
public calss SimpleProfiler {
public Object profile(ProceedingJoinPoint call, String name, int age) throws Throwable {
StopWatch clock = new StopWatch("Profiling for" + name + " and " + age + "");
try {
clock.start(call.toShortString());
return call.proceed();
}finally {
clock.stop();
System.out.println(clock.prettyPrint());
}
}
}
5-6 Introductions(配置)
1.简介允许一个切面来声明一个实现指定接口的通知对象(即一个声明了通知的切面,该通知实现了指定接口),并且提供了一个接口实现类来代表这些对象
2.它由<aop:aspect>中的<aop:declare-parents>元素声明,该元素用于声明所匹配的类型拥有一个新的parent(因此得名)
3.配置
<aop:aspect id="usageTrackerAspect" ref="usageTracking">
<aop:declare-parents //声明一个通知对象
types-matching="com.xyz.myapp.service.*(+)" //匹配该包下的所有类的方法
implement-interface="com.xyz.myapp.service.tracking.UsageTracked" //该通知对象实现了指定接口
default-impl="com.xyz.myapp.service.tracking.DefaultUsageTracked"/> //提供了接口实现类
<aop:before
pointcut="com.xyz.myapp.SystemArchitecture.businessService() and this(usageTracked)"
method="recordUsage"/>
</aop:aspect>
当匹配到符合的service的类时,会为它赋予一个新的parent——UsageTracked,这样在使用时,可以对类(以新的父类接口)进行强转,然后可以调用接口的方法(实现是指定的实现类的实现)
4.使用
public void recordUsage(UsageTracked usageTracked) {
usageTracked.incrementUseCount();
}
UsageTracked usageTracked =(UsageTracked) context.getBean("myService");
5.所有基于配置文件(schema-defined)的aspents,只支持singleton model
5-7 Advisors
1.advisor就像一个小的自包含的方面,只有一个advice
2.切面自身通过一个bean表示,并且必须实现某个advice接口,同时,advisor也可以很好地利用AspectJ的切入点表达式
3.Spring通过配置文件中的<aop:advisor>元素支持advisor。实际使用中,大多数情况下它会和transactional advice(事务相关的advice)配合使用
4.为了定义一个advisor的优先级以便让advice可以有序,可以使用order属性来定义advisor的顺序
5.例子
<aop:config>
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service..(..))"/>
<aop:advisor
pointcut-ref="businessService"
advice-ref="tx-advice"/>
</aop:config>
<tx:advice id="tx-advice"> //事务相关
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx"attributes>
</tx:advice>
第六章 Spring AOP的API介绍(了解,常用的还是在xml中进行配置)
6-1 Spring AOP API的Pointcut、advice概念及应用
1.Pointcut
2.Before advice
3.Throws advice
4.After Returning advice
5.Interception around advice
6.Introduction advice
7.Advisor
6-2 ProxyFactoryBean及相关内容(上)
1.创建Spring AOP代理的基本方法是使用org.springframework.aop.framework.ProxyFactoryBean
2.(即通过引入中间层)这可以完全控制切入点和通知(advice)以及他们的顺序
3.假如定义了一个bean id为foo的ProxyFactoryBean,那么引用foo对象时,看到的将不是ProxyFactoryBean本身的实例,而是ProxyFactoryBean实现里getObject()方法创建的对象,getObject方法将创建一个AOP代理来包装一个目标对象。通过这种方式来达到代理的目的
4.
(1)使用ProxyFactoryBean或者其它IoC相关类来创建AOP代理的最重要好处是通知和切入点也可以由IoC来管理
(2)如果被代理类没有实现任何借口,则使用CGLIB代理,否则使用JDK代理
(3)通过设置proxyTargetClass为true,可强制使用CGLIB
(4)如果目标类实现了一个(或者多个)接口,那么创建代理的类型将依赖ProxyFactoryBean的配置
(5)如果ProxyFactoryBean的proxyInterfaces属性被设置为一个或多个全限定接口名,基于JDK的代理将被创建
(6)如果ProxyFactoryBean的proxyInterfaces属性没有被设置,但是目标类实现了一个(或更多)接口,那么ProxyFactoryBean将自动检测这个类已将实现了至少一个接口,创建一个基于JDK的代理
5.创建基于接口的代理——代码
<bean id="personTarget" class="com.mycompany.PersonImpl">
<property name="name" value="Tony"/>
<preperty name="age" value="51"/>
</bean>
<bean id="myAdvisor" class="com.mycompany.MyAdvisor">
<property name="someProperty" value="Custom string property value"/>
</bean>
<bean id="debugInterceptor" class="org.spring.aop.interceptor.DebugInterceptor">
</bean>
//不是直接把Person类赋给person,而是中间有一个代理层
<bean id="person" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="com.mycompany.Person"/>
<property name="target" ref="personTarget"/>
<property name="interceptorNames">
<list>
<value>myAdvisor</vlaue>
<value>debugInterceptor</value>
</list>
</property>
</bean>
//其它bean调用时
<bean id="personUser" class="com.mycompany.PersonUser">
<property name="person"><ref bean="person"/></property>
</bean>
6.使用匿名内部bean来隐藏目标和代理之间的关系(即不用reference,直接在内部定义bean)(推荐使用)
<bean id="person" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="com.mycompany.Person"/>
<property name="target"
<bean class="com.mycompany.PersonImpl">
<property name="name" value="Tony"/>
<preperty name="age" value="51"/>
</bean>
<property name="interceptorNames">
<list>
<value>myAdvisor</vlaue>
<value>debugInterceptor</value>
</list>
</property>
</bean>
6-3 ProxyFactoryBean及相关内容(下)
1.Proxying classes(Proxy类)
(1)前面的例子中如果没有Person接口,则Spring会使用CGLIB代理,而不是JDK动态代理
(2)如果想,可以强制在任何情况下使用CGLIB,即使有接口
(3)CGLIB代理的工作原理是在运行时生成目标类的子类,Spring配置这个生成的子类委托方法调用到原来的目标
(4)子类是用来实现Decorator模式,织入通知
(5)CGLIB的代理对用户是透明的,需要注意:
1°final不能被通知,因为它们不能被覆盖
2°不需要把CGLIB添加到classpath中,在Spring3.2中,CGLIB被重新包装并包含在Spring核心的JAR(即基于CGLIB的AOP就像JDK动态代理一样“开箱即用”)
2.使用global advisors
用*做通配,匹配所有拦截器加入通知链(即实现Interceptor接口的),例:
<list>
<value>mooc*</vlaue>
</list>
3.简化的proxy定义
使用父子bean定义,以及内部bean定义,可能回带来更清洁和更简洁的代理定义(抽象属性标记父bean定义为抽象的这样它不能被实例化)
4.使用ProxyFactory
(1)好处:使用Spring AOP而不必依赖于Spring IoC
ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl);
factory.addAdvice(myMethodInterceptor);
factory.addAdvisor(myAdvisor);
MyBusinessInterface tb = (MyBusinessInterface) factory.getProxy();
(2)大多数情况下最佳实践是用IoC容器创建AOP代理
(3)虽然可以硬编码方式实现,但是Spring推荐使用配置或注解方式实现
5.使用"auto-proxy"
(1)Spring也允许使用“自动代理”的bean定义,它可以自动代理选定的bean,这是建立在Spring的"bean psot processor"功能基础上的(这个功能在加载bean的时候就可以修改)
(2)通过BeanNameAutoProxyCreator实现
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="jdk*,onlyJdk"/> //自动代理jdk开头的或onlyJdk的所有bean
<property name="interceptorNames">
<list>
<value>myInterceptor</value>
<list>
<property>
</bean>
(3)通过DefaultAdvisorAutoProxyCreator实现,则当前IoC容器中自动应用来达到创建代理的效果,不用显示声明引用advisor的bean定义
第七章 Spring对AspectJ的支持
7-1 AspectJ介绍及Pointcut注解应用
1.AspectJ
(1)@AspectJ的风格类似纯java注解的普通java类
(2)Spring可以使用AspectJ来做切入点解析
(3)AOP的运行时仍旧是纯的Spring AOP,对AspectJ的编译器或者织入无依赖性
2.Spring中配置@AspectJ
(1)对@AspectJ支持可以使用xml或java风格的配置
(2)确保AspectJ的aspectjweaver.jar库包含在应用程序(版本1.6.8或更高版本)的classpath中
1°注解
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}
2°xml配置
<aop:aspectj-autoproxy/>
3.@Aspect注解
(1)@AspectJ切面使用@Aspect注解配置,拥有@Aspect的任何bean将被Spring自动识别并应用
(2)用@Aspect注解的类可以有方法和字段,它们也可能包括切入点(pointcut),通知(Advice)和引入(introduction)声明
(3)@Aspect注解是不能够通过类路径自动检测发现的,所以@Aspect需要配合使用@Component注释或者在xml配置bean,如:
1°
<bean id="myAspect" class="org.xyz.NotVeryUserfulAspect">
//配置aspect的属性
</bean>
@Aspect
public class NotVeryUserfulAspect {
}
2°
@Component
@Aspect
public class NotVeryUserfulAspect {
}
(4)一个类中的@Aspect注解将标识它为一个切面,并且将自己从自动代理中排除(不会代理自己)
4.如何定义切入点pointcut
(1)一个切入点通过一个普通的方法定义来提供,并且切入点表达式使用@Pointcut注解,方法返回类型必须为void
如:
@Pointcut("execution(* transfer(..))")
private void anyOldTransfer() {}
定义一个名为anyOldTransfer的方法(即一个切入点),这个切入点将匹配任何名为"transfer"的方法
5.切入点支持哪些定义方式(哪些点可以定义)
execution:匹配方法执行的连接点
within:限定匹配特定类型的连接点
this:匹配特定连接点的bean引用是指定类型的实例的限制
target:限定匹配特定连接点的目标对象时指定类型的实例
args:限定匹配特定连接点的参数是指定类型的实例
@target:限定匹配特定连接点的类执行对象的具有给定类型的注解
@args:限定匹配特定连接点实际传入参数的类型具有给定类型的注解
@within:限定匹配到内具有给定的注释类型的连接点
@annotation:限定匹配特定连接点的主体具有给定的注解
6.组合pointcut
(1)切入点表达式可以通过&&、||和!进行组合,也可以通过名字引入切入点表达式
(2)通过组合,可以建立更为复杂的切入点表达式,如:
@Pointcut("execution(public * (..))")
private void anyPublicOperation() {}
@Pointcut("within(com.xyz.comeapp.trading..)")
private void inTrading() {}
@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation() {}
7.如何定义良好的pointcuts
(1)AspectJ是编译期的AOP
(2)检查代码并匹配连接点与切入点的代价是昂贵的
(3)一个好的切入点应该包含以下几点
1°选择特定类型的切入点,如:execution、get、set、call、handeler
2°确定连接点范围,如:within、withincode
3°匹配上下文信息,如:this、target、@annotation
7-2 Advice定义及实例
1.Before advice
(1)例子
1°直接定义execution表达式
@Component
@Aspect
public class MoocAspect {
@Before("execution(* com.imooc.aop.aspectj.biz.*Biz.*(..))*)
public void before() {
//…
}
}
在执行com.imooc.aop.aspectj包下以Biz结尾的类的所有方法时匹配Advice
2°引用一个已经存在的pointcut
@Component
@Aspect
public class MoocAspect {
@Pointcut("execution(* com.imooc.aop.aspectj.biz.*Biz.*(..))")
public void pointcut() {}
@Before("execution(*pointcut()")
public void before() {
//…
}
}
2.After returning advice
(1)有时候需要在通知体内得到返回的实际值,可以使用@AfterReturning绑定返回值的类型
@AfterReturning(pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()", returning="retVal")
public void do AccessCheck(Object retVal) {
//…
}
将@AfterReturning注解中的返回值在下面的方法中声明,不确定返回值的类型则为Object
3.After throwing advice
4.After(finally) advice
(1)最终通知必须准备处理正常和异常两种返回情况,它通常用于释放资源
5.Around advice
(1)环绕通知使用@Around注解来声明,通知方法的第一个参数必须是ProceedingJoinPoint类型
(2)在通知内部调用ProceedingJoinPoint的proceed()方法会导致执行真正的方法,传入一个Object[]对象,数组中的值将被作为参数传递给方法
7-3 Advice扩展
1.给advice传递参数
2.Advice的参数及泛型
3.Advice参数名称
(1)通知和切入点注解有一个额外的"argNames"属性,它可以用来指定所注解的方法的参数名,如:
@Before(vlaue="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)", argNames="bean,auditable")
public void audit(Object bean, Auditable auditable) {
AuditCode code = auditable.value();
// … use code and bean
}
(2)如果第一个参数是JointPoint、ProceedingJoinPoint、JoinPoint.StaticPart,那么可以忽略它
4.Introductions
(1)允许一个切面声明一个通知对象实现指定接口,并且提供了一个接口实现类来代表这些对象
(2)AspectJ中使用@DeclareParents来对introduction进行注解,这个注解用来定义匹配的类型拥有一个新的parent
5.切面实例化模型
(1)这是一个高级主题
(2)"perthis"切面通过指定@Aspect注解perthis子句实现
(3)每个独立的service对象执行时都会创建一个切面实例
(4)service对象的每个方法在第一次执行的时候创建切面实例,切面在service对象失效的同时失效
@Aspect("perthis(com.xyz.myapp.SystemArchitecture.businessService())")
public class MyAspect {
private int someState;
@Before(com.xyz.myapp.SystemArchitecture.businessService())
public void recordServiceUsage() {
//…
}
}
查看全部
举报