申明一个运行时使用,然后运用在方法上
Advice扩展
一、给advice传递参数
【a】方式一:在通知注解里写参数,通过&& args(参数名,..)传入参数,该参数可以是自己定义的类型,也可以为String等类型。(通常获取参数,进行一些判断或者日志的记录)

【a】方式二:切入点和通知分开写的方式,在切入点注解里写参数,在通知注解里引用切入点注解的方法名。
【b】运行时,生效的一个注解,这两个注解修饰在方法上,在通知注解里通过&& @annotation(运行时注解修饰的方法名,首字母小写),如果某个方法使用了运行时注解,则一定会给value()附一个值,在通知方法里可以通过方法参数,调用该方法得到该值(一般记录该注解用了哪些方法,或者用在判断该方法上是否加了某注解,后者判断获取到的值是哪值)


在这里我直接获取了需要传递的对象,这时候方法体并没有执行,我的通知应该可以获取此时未执行数据库的数据。
切面实例化模型
Introductions
Advice参数名称
Advice参数及泛型
给Advice传递参数
vbnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
vbbbbbbbbbbbbbbbb
ggggggggggggggggggggg
dsdddddddddddddddddddddddddd
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() {
//…
}
}
Spring相关知识
第一章 概述
1-1 Spring入门课程简介
SpringFrameWork
1-2 Spring概况
1.一个轻量的控制反转(IoC)和面向切面(AOP)的容器框架
(1)从大小和开销两方面而言,Spring是轻量的
(2)通过控制反转达到松耦合的目的
(3)提供面向切面编程的丰富支持,允许通过分离应用的业务逻辑(如商品的入库)与系统级服务(如日志记录)进行内聚性的开发
(4)包含并管理应用对象的配置和生命周期,这个意义上是一种容器(容器:用来管理对象)
(5)将简单的组件配置、组合成为复杂的应用,这个意义上是框架(框架:半成品,支持其他组件的组合)
2.Spring作用
容器、提供对多种技术的支持、AOP(事务管理、日志)、提供了众多方便应用的辅助类(JDBC 模板等)、对主流应用框架(MyBatis)提供了良好的支持
3.适应范围
(1)构建企业应用(SpringMVC+Spring+MyBatis)
(2)单独使用Bean容器进行管理
(3)单独使用AOP进行切面处理
(4)其他的Spring功能,如:对消息的支持
(5)在互联网中的应用
1-3 Spring框架
1.框架
(1)定义:一套规范或规则,程序员在该规范或规则下工作
(2)特性:半成品,封装了特定的处理流程和控制逻辑,成熟的、不断升级改进的软件
(3)与类库的区别:框架一般是封装了逻辑、高内聚的,类库则是松散的工具组合;框架专注于某一领域,类库则是更通用的
(4)为什么使用框架:软件系统日趋复杂;重用度高,开发效率和质量提高;软件设计人员要专注于对领域的了解,使需求分析更充分;易于上手、快速解决问题
第二章 Spring IoC容器
2-1 IoC及Bean容器
1.接口
用于沟通的中介物(规范)的抽象化
对外提供一些功能,内部的实现不公开
Java中,接口即声明,声明了哪些方法是对外公开提供的
Java8中,接口可以有方法体
2.面向接口编程
结构设计中,分清层次及调用关系,每层只向外(上层)提供一组功能接口,各层间仅依赖接口而非实现类
接口实现的变动不影响各层间的调用,这一点在公共服务中尤为重要
“面向接口编程”中的“接口”是用于隐层具体实现和实现多态性的组件
例子:
接口Dao,实现类DaoImpl,使用方法为多态Dao dao = new DaoImpl();用接口声明,将接口的实现类赋值给接口,最后调方法
3.IoC
IoC:控制反转,控制权的转移,应用程序本身不负责依赖对象的创建和维护,而是由外部容器负责创建和维护(住房子不是自己来建,而是找中介——中介找房子——住中介找的房子,即IoC的动作:找IoC容器——容器返回对象——使用对象)
DI:依赖注入,是IoC的一种实现方式,目的是创建对象并组装对象之间的关系
IoC中,所有的容器称为Bean
4.Bean容器的初始化
(1)基础:两个包org.springframework.beans和org.springframeword.context
BeanFactory提供配置结构和基本功能,加载并初始化Bean
ApplicationContext是BeanFactory的子接口,保存了Bean对象并在Spring中被广泛使用
他们都可以代表Spring容器,Spring容器是生成Bean实例的工厂,并管理Bean
在创建Spring容器的实例时(通过getBean方法),必须提供Spring容器管理的Bean的详细配置信息,Spring的配置信息通常通过xml配置文件来设置(也可通过注解)。
在实际的应用中,Spring容器通常是采用声明式方式配置产生:即开发者只要在web.xml文件中配置一个Listener,该Listener将会负责初始化Spring容器。
实现BeanFactoryAware接口的Bean实例,拥有访问BeanFactory容器的能力
就是说,ApplicationContext用来管理Bean,可以通过xml配置文件或注解的方式来将Bean注册到ApplicationContext中
(2)初始化ApplicationContext的方式
1°本地文件
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("F:/workspace/ applicationContext.xml");
2°Classpath
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
3°Web应用中依赖servlet或Listener
a.
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener<listener-class>
<listener>
b.
<servlet>
<servlet-name>context<servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet<servlet-class>
<load-on-startup>1<load-on-startup>
<servlet>
2-2 Spring注入方式
1.指在启动Spring容器加载bean配置的时候,完成对变量的赋值行为
常用的两种注入方式:
设值注入
构造注入
2.设值注入(即调用set方法,需要在InjectionServiceImpl类中提供set方法来获取InjectionDAO的对象)
<bean id="injectionService" class="com.imooc.injection.serivec.InjectionServiceImpl">
<property name="injectionDAO" ref="injectionDAO"/>
或<property name="injectionDAO" ref="injectionDAO"></property>
</bean>
<bean id="injectionDAO" class="com.imooc.ioc.injection.dao.InjectionDAOImpl"></bean>
3.构造注入(即调用构造方法,需要在InjectionServiceImpl类中提供构造方法来获取InjectionDAO的对象)
<bean id="injectionService" class="com.imooc.injection.serivec.InjectionServiceImpl">
<constructor-arg name="injectionDAO" ref="injectionDAO"/>
<constructor-arg name="injectionDAO" ref="injectionDAO"></constructor-arg>
</bean>
<bean id="injectionDAO" class="com.imooc.ioc.injection.dao.InjectionDAOImpl"></bean>
第三章 Spring Bean装配(上)
主要部分:
Bean配置项
Bean的作用域
Bean的生命周期
Bean的自动装配
Resource和ResourceLoader
Bean的注解方式(对以上内容用注解方式进行改进)
3-1 Spring Bean装配之Bean的配置项和作用域
1.Bean的配置项
(1)Id:Bean的唯一标识
(2)Class:具体要实例化的类
(3)Scope:作用域(范围)
(4)Constructor arguments:构造器参数
(5)Properties:属性
(6)Autowiring mode:自动装配模式
(7)lazy-initialization mode:懒加载模式
(8)Initialization/destruction method:初始化/销毁方法
2.Bean的作用域
(1)singleton:(默认作用域)单例,指一个Bean容器中只存在一份
(2)prototype:每次请求(每次使用)创建新的实例,destory方法不生效(因为会自动回收)
(3)request:每次http请求创建一个实例且仅在当前request内有效
(4)session:同上,每次http请求创建,当前session内有效(session在一个会话周期有效)
(5)global session:基于portlet的web中有效(protlet定义了global session),如果是在web中,同session(假如有多个系统,global session跨越多个session)
3-2 Spring Bean装配之Bean的生命周期
1.Bean的生命周期
定义
初始化
使用
销毁
2.单一Bean初始化的两种方式
(1)实现org.springframework.beans.factory.InitializingBean接口,覆盖afterPropertiesSet方法
public class ExampleInitializingBean implements IntializingBean {
@Override
public void afterPropertiesSet() throws Exception {
//do something
}
}
(2)配置init-method
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {
public void init() {
//do some initialization work
}
}
3.单一Bean销毁的两种方式
(1)实现org.springframework.beans.factory.DisposableBean接口,覆盖destory方法
public class ExampleDisposableBean implements DisposableBean {
@Override
public void destory() throws Exception {
//do something
}
}
(2)配置destory-method
<bean id="exampleInitBean" class="examples.ExampleBean" destory-method="cleanup"/>
public class ExampleBean {
public void cleanup() {
//do some destructiono work (like relesing polled connections)
}
}
4.配置全局默认的初始化、销毁方法(所有Bean都会调用)
在Bean的配置文件的最外层(最上层),紧接着xmlns(XML的命名空间)和xsi:schemaLocation后面,添加
default-init-method="init" default-destory-method="destory">
5. 优先级
实现接口的方式>配置init-method和destory-method的方式
如果配置了单一Bean的初始化/销毁方式,那么全局默认的初始化/销毁方式不会生效
3-3 Spring Bean装配之Aware接口
1.Aware
(1)Spring中提供了一些以Aware结尾的接口,实现了Aware接口的Bean在被初始化之后,可以获取相应资源
(2)通过Aware接口,可以对Spring相应资源进行操作(一定要慎重)
(3)为对Spring进行简单的扩展提供了方便的入口
如:ApplicationContextAware、BeanNameAware
Day45(8.13)
3-4 Spring Bean装配之自动装配(Autowiring,即自动注入)
1.自动装配的类型
(1)No:不做任何操作
(2)byname:根据属性名自动装配。此选项将检查容器并根据名字查找与属性完全一致的bean,并将其与属性自动装配
(3)byType:如果容器中存在一个与指定属性类型相同的bean,那么将与该属性自动装配;如果存在多个该类型bean,那么抛出异常,并指出不能使用byType方式进行自动装配;如果没有找到相匹配的bean,则什么事都不发生
(4)Constructor:与byType方式类似,不同之处在于他应用于构造器参数。如果容器中没有找到与构造器参数类型一致的bean,那么抛出异常
2.个人理解
这种方式是:对于一个需要注入的bean,不需要在它的bean中添加property或constructor-arg配置(手动注入),而是在xml配置文件中设置autowire来实现自动注入,自动调用setter方法或构造方法(注意,仍然需要setter方法或构造方法),如:
原来是:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=…
…>
<bean id="injectionService" class="com.imooc.injection.serivec.InjectionServiceImpl">
<property name="injectionDAO" ref="injectionDAO"/>
</bean>
<bean id="injectionDAO" class="com.imooc.ioc.injection.dao.InjectionDAOImpl"></bean>
现在是:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=…
…
default-autowire="byName">
<bean id="injectionService" class="com.imooc.injection.serivec.InjectionServiceImpl"></bean>
<bean id="injectionDAO" class="com.imooc.ioc.injection.dao.InjectionDAOImpl"></bean>
3-5 Spring Bean装配之Resources
1.针对于资源文件的统一接口(获取资源文件)
2.Resources的类型
(1)UrlResource:URL对应的资源,根据一个URL地址既可创建
(2)ClassPathResource:获取类路径下的资源文件
(3)FileSystemResource:获取文件系统里面的资源
(4)ServletContextResource:ServletContext封装的资源,用于访问ServletContext环境下的资源
(5)InputStreamResource:针对于输入流封装的资源
(6)ByteArrayResource:针对于字节数组封装的资源
3.Resources的加载类ResourceLoader
所有application context都实现了ResourceLoader接口,所以都可以用来获取Resource实例
获取Resource对象举例:
(可以通过实现ApplicationContextAware接口)在获取applicationContext对象后,就可以获取资源
Resource template = applicationContext.getResource("some/resource/path/myTemplate.txt");
Resource template = applicationContext.getResource("classpath:some/resource/path/myTemplate.txt");
Resource template = applicationContext.getResource("file:/some/resource/path/myTemplate.txt");
4.ResourceLoader的前缀
前缀 例子
classpath: classpath:com/myapp/config.xml
file: file:/data/config.xml
http: http://myserver/logo.png
(none): /data/config.xml(此时依赖于ApplicationContext所依赖的,例如如果是classpath就是classpath)
第四章 Spring Bean装配(下)
4-1 Spring Bean装配之Bean的定义及作用域的注解实现
1.Classpath扫描与组件管理
(1)从Spring3.0开始,Spring JavaConfig项目提供了很多特性,包括使用java而不是xml定义bean,比如@Configuration、@Bean、@Import、@DependsOn
(2)Component是一个通用注解,可用于任何bean
(3)Repository、@Service、@Controller是更有针对性的注解
@Repository通常用于注解DAO类,即持久层
@Service通常用于注解Service类,即服务层
@Controller通常用于注解Controller类,即控制层(MVC)
2.元注解
即注解的注解,如:
@Component
public @interface Service {
//…
}
对于Service这个注解,可以用Component注解进行注解
3.类的自动检测及Bean的注册
Spring可以自动检测类并注册Bean到ApplicationContext中(通过注解的方式)
4.<context:annotation-config/>
通过在基于xml的Spring配置如下标签(请注意:在头文件中加上该标签,会同时自动加入context命名空间)
<context:annotation-config/>,;仅会查找在同一个applicationContext中的bean注解
5.类的自动检测及Bean的注册
(1)为了检测类并注册相应的Bean,需要在文件头中的下面添加一行(component-scan即对注解进行扫描,如果扫描到注解对应的类,将该类注册到容器上;base-package是扫描该包下的类):
<context:component-scan base-package="com.service"/>
(2)<context:component-scan>包含<context:annotation-config>,通常在使用前者后,不用再使用后者(前者对类和类内的注解进行扫描,后者只对类内的方法或成员变量的注解进行扫描)
6.使用过滤器进行自定义扫描
(1)默认情况下,类被自动发现并注册bean的条件是:使用@Component、@Repository、@Service、@Controller注解或使用@Component的自定义注解
(2)可以通过过滤器修改上面的自动发现行为,如:
<beans>
<context:component-scan base-package="org.example">
<context:include-filter type="regex"
expression=".*Stub.*Repository"/>
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
</beans>
本例的xml配置忽略所有的@Repository注解并寻找所有使用"Stub"注解的类
(3)还可以使用use-default-filters="false"禁止自动发现与注册
7.过滤器的类型
annotation、assignable、aspectj、regex、custom
8.如何自定义Bean
(1)扫描过程中组件被自动检测,那么Bean名称是由BeanNameGenerator生成的(@Component、@Repository、@Service、@Controller都会有个name属性用来显示设置Bean Name),如果不显式指定name,则用默认生成的(默认为首字母小写的类名),如:
@Service("myMovieListener")
public class SimpleMovieListener {
//…
}
和
@Service() //此处的name默认为simpleMovieListener
public class SimpleMovieListener {
//…
}
此处的名称相当于xml配置文件中的id
(2)可以自定义Bean的默认命名策略,如:
<beans>
<context:component-scan base-package="org.example"
name-generator="org.example.MyNameGenerator"/>
</beans>
要实现BeanNameGenerator接口,并一定要包含一个无参构造器,
9.作用域(Scope)
(1)通常情况下自动查找的Spring组件,其scope是singleton,Spring2.5提供了一个标识scope的注解@Scope
@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
//…
}
(2)也可以自定义scope策略,如
<beans>
<context:component-scan base-package="org.example"
scope-resolver="org.example.MyScopeResolver"/>
</beans>
要实现ScopeMetadataResolver接口并提供一个无参构造器
10.代理方式(代理即代理网络用户去取得网络信息)
可以使用scoped-proxy属性指定代理,有三个值可选:no、interfaces、targetClass(目标类),如:
<beans>
<context:component-scan base-package="org.example"
scoped-proxy="interfaces"/>
</beans>
4-2 Spring Bean装配之Autowired注解说明-1
1.@Required
(1)Required注解适用于bean属性的setter方法
(2)这个注解仅仅标识,受影响的bean属性必须在配置时被填充,通过在bean定义或通过自动装配一个明确的属性值如:
public class SimpleMovieListener {
private MovieFinder movieFinder;
@Required
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
//…
}
2.@Autowired(不需要在xml中使用autowire属性,添加注解即可实现自动注入)
个人理解:在A类的方法中需要B类的对象,要通过注入的方式实现,最普通的方式是在xml中添加配置,也可以在xml中设置autowire属性来自动注入,还可以用添加注解的方式来自动注入
(1)可以将@Autowired注解理解为“传统”的setter方法,如:
private MovieFinder movieFinder;
@Autowired
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
(2)也可以用于构造器或成员变量,如:
@Autowired
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPerferenceDao;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this. customerPreferenceDao = customerPreferenceDao;
}
(3)默认情况下,如果找不到合适的bean,将会导致autowiring失败并抛出异常,可以通过下面的方式避免:
public class SimpleMovieListener {
private MovieFinder movieFinder;
@Autowired(required=false)
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
//…
}
注意:
1°每一个类只能有一个构造器被标记为required=true
2°@Autowired的必要属性,建议使用@Required来注解
4-3 Spring Bean装配之Autowired注解说明-2
1.@Autowired
(1)可以使用@Autowired注解那些众所周知的解析依赖性接口,比如:BeanFactory, ApplicationContext, Environment, ResourceLoader, ApplicationEventPublisher, MessageSource,例:
public class MovieRecommender {
@Autowired
private ApplicationContext context;
public MovieRecommender() {
}
//…
}
通过注解,可以得到ApplicationContext并使用
(2)可以通过添加注解给需要该类型的数组的字段或方法,以提供ApplicationContext中的所有的特定类型的bean,
1°可以用于装配Set,如:
private Set<MovieCatalog> movieCatalogs;
@Autowired
public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
this. movieCatalogs = movieCatalogs;
}
通过@Autowired注解后,ApplicationContext中的所有符合Set的泛型声明的bean或其子类将会放到Set中去
2°可以用于装配key为String的Map(key即所有Bean的id,value即Bean的对象),如:
private Map<String, MovieCatalog> movieCatalogs;
@Autowired
public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
this. movieCatalogs = movieCatalogs;
}
3°如果希望list数组有序,可以让bean实现org.springframe.core.Ordered接口或使用@Order注解
如:@Order(value=1)对应的bean,排在@Order(value=2)对应的bean之前
(3)注意:
@Autowired注解是由Spring BeanPostProcessor处理的,所以不能在自己的BeanPostProcessor或BeanFactoryPostProcessor类型中应用这些注解,这些类型必须通过xml或Spring的@Bean注解加载
(4)例子
定义一个接口和他的两个实现类,都用@Component进行注解(来让Spring发现并注册为bean),然后定义一个list<接口>并用@Autowired进行注解,这样就可以将这两个实现类注入到list中
4-4 Spring Bean装配之Autowired注解说明-3
1.@Qualifier
(1)定义
按类型自动装配时,可能有多个bean实例的情况,这时可以使用Spring的@Qualifier注解来缩小范围(或指定唯一),也可以用于指定单独的构造器或方法参数
可以用于注解集合类型变量
(2)使用举例
1°用于成员变量
对成员变量用@Qualifier进行注解,当有多个MovieCatalog想自动注入到bean时,只将id为main的注入到bean
public class MovieRecommender {
@Autowired
@Qualifier("main")
private MovieCatalog movieCatalog;
}
2°用在方法中(最常用)
@public void prepare(@Qualifier("main") MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}
3°在xml中使用
<bean class="example.SimpleMovieCatalog">
<qualifier value="main"/>
</bean>
(3)
如果通过名字进行注解注入,主要使用的不是@Autowired(即使在技术上能够通过@Qualifier执行bean的名字),替代方式是使用JSR-250标准中的@Resource注解,它是通过其独特的名称来定义来识别特性的名称(这是一个与所声明的类型无关的匹配过程)
因语义差异,集合或Map类型的bean无法通过@Autowired来注入,这是因为没有类型匹配到这样的bean。为这些bean使用@Resource注解,可以通过唯一名称引用集合或Map的bean。
@Autowired适用于fields,constructors,multi-argument methods这些允许在参数级别使用@Qualifier注解缩小范围的情况
@Resource适用于成员变量、只有一个参数的setter方法,所以在目标是构造器或一个多参数方法时,最好的方式使用qualifiers
(4)定义自己的qualifier注解并使用
定义:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {
String value();
}
使用:
public class MovieRecommender {
@Autowired
@Genre("Action")
private MovieCatalog comedyCatalog;
@Atuowired
public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) {
this.comedyCatalog = comedyCatalog;
}
}
在xml中定义
<bean class="example.SimpleMovieCatalog">
<qualifier type="Genre" value="Action"/>
</bean>
4-5 Spring Bean装配之基于Java的容器注解说明——@Bean
1.定义
(1)Bean标识一个用于配置和初始化一个由SpringIoC容器管理的新对象的方法,类似于XML配置文件的<bean/>
(2)可以在Spring的@Component注解的类中,使用@Bean注解任何方法(仅仅是可以)
(3)上一点中,通常使用的是@Configuration(即将该类当做一个配置文件来使用)
(4)如:
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
等价于
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
2.自定义Bean name
public class AppConfig {
@Bean(name="myFoo")
public Foo foo() {
return new Foo();
}
}
3.可以使用init-method和destory-method
public class Foo {
public void init() {
//初始化逻辑
}
}
public class Bar {
public void cleanup() {
//销毁逻辑
}
}
@Configuration
public class AppConfig {
@Bean(initMethod="init")
public Foo foo() {
return new Foo();
}
@Bean(destoryMethod="cleanup")
public Bar bar() {
return new Bar();
}
}
4-6 Spring Bean装配之基于Java的容器注解说明——@ImportResource和@Value(进行资源文件读取)
1.使用xml来进行资源文件的读取
<beans>
<context:annotation-config/>
//使用property-placeholder并指定location,来加载properties文件
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
<bean class="com.acme.AppConfig"/>
<bean class-"org.springframework.jdbc.datasource.DriverManagerDataSource">
property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
</beans>
2.使用注解进行资源文件的读取
@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml") //导入资源文件
public class AppConfig {
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource() { //创建bean并把成员变量赋给它
return new DriverManagerDataSource(url, username, password);
}
}
注意:在jdbc.properties中,要用jdbc.username,因为username是当前数据库的用户的用户名,而jdbc.username是配置文件中的用户名
4-7 Spring Bean装配之基于Java的容器注解说明——@Bean和@Scope
1.@Bean默认是单例的,使用@Scope可以改变,如:
@Configuration
public class MyConfiguration {
@Bean
@Scope("prototype")
public Encryptor encryptor() {
//…
}
}
或
@Bean
@Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
public UserPerferences userPreferences() {
return new UserPreferences;
}
4-8 Spring Bean装配之基于Java的容器注解说明——基于泛型的自动装配
1.使用
@Configuration
public class MyConfiguration {
@Bean
public StringStore stringStore() {
return new StringStore();
}
@Bean
public IntegerStore integerStore() {
return new IntegerStore();
}
}
(1)方式1
@Autowired
private Store<String> s1;
@Autowired
private Store<Integer> s2;
(2)方式2
@Autowired
private List<Store<Integer>> s;
2.@Autowired的拓展内容——自定义qualifier注解
(1)CustomAtuowireConfigurer是BeanFactoryPostProcessor的子类,通过它可以注册自己的qualifier注解类型(即使没有通过Spring的@Qualifier注解)
<bean id="customAutowireConfigurer"
class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
<property name="customQualifierTypes"> //用来放类型
<set> //这是set,可以放多个类型
<value>example.CustomQualifier</value>
</set>
</property>
</bean>
(2)该AutowireCandidateResolver决定自动装配的候选者:
1°每个bean定义的autowire-candidate值
2°或任何<bean/>中的default-autowire-candidates
3°或@Qualifier注解及使用CustomAutowireConfigurer的自定义类型
4-9 Spring Bean装配之Spring对JSR支持的说明
JSR:Java规范提案(任何人都可以提交“规范提案”,然后经过委员会审核,随着技术的发展,会围绕其中某些规范做出对应的实现)
1.@Resource
(1)Spring支持使用JSR-250@Resource注解的变量或setter方法,这是一种在Java EE 5和6的通用模式,Spring管理的对象也支持这种模式
(2)@Resource有一个name属性,并且默认Spring解释该值作为被注入bean的名称
public class SimpleMovieListener {
private MovieFinder movieFinder;
@Resource(name="myMovieFinder")
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
(3)如果没有显示地指定@Resource的name,默认的名称是从属性名或者setter方法得出
public class SimpleMovieListener {
private MovieFinder movieFinder;
@Resource
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
(4)注解提供的名字被解析为一个bean的名称,这是由ApplicationContext中的CommonAnnotationBeanPostProcessor发现并处理的
2.@PostConstruct和@PreDestory
(1)CommonAnnotationBeanPostProcessor不仅能识别JSR-250中的声明周期注解@Resource,在Spring2.5也引入支持了初始化回调和销毁回调(回调即执行一个方法前/后先执行回调方法),前提是CommonAnnotationBeanPostProcessor是在Spring的ApplicationContext中注册的,如:
public class CachingMoiveListener {
@PostConstruct
public void populatemovieCache() {
//…
}
@PreDestory
public void clearMovieCache() {
//…
}
}
3.JSR330的标准注解
(1)从Spring3.0开始支持JSR330标准注解(依赖注入注解),其扫描方式与Spring注解一致
(2)使用JSR330需要依赖javax.inject包
(3)使用Maven引入方式
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
4.@Inject
(1)@Inject等效于@Autowired,可以使用于类、属性、方法、构造器
import javax.inject.Inject;
public class SimpleMovieListener {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
5.@Named
(1)如果在一个IoC容器中有多个同类型的bean,如果想使用特定名称进行依赖注入,使用@Named
(2)@Named与@Component是等效的
(3)使用方式
1°
import javax.inject.Inject;
import javax.inject.Named;
public class SimpleMovieListener {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(@Named("main") MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
2°
@Named("movieListener")
public class SimpleMovieListener {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
Advice扩展
一、给advice传递参数
【a】方式一:在通知注解里写参数,通过&& args(参数名,..)传入参数,该参数可以是自己定义的类型,也可以为String等类型。(通常获取参数,进行一些判断或者日志的记录)

【a】方式二:切入点和通知分开写的方式,在切入点注解里写参数,在通知注解里引用切入点注解的方法名。
【b】运行时,生效的一个注解,这两个注解修饰在方法上,在通知注解里通过&& @annotation(运行时注解修饰的方法名,首字母小写),如果某个方法使用了运行时注解,则一定会给value()附一个值,在通知方法里可以通过方法参数,调用该方法得到该值(一般记录该注解用了哪些方法,或者用在判断该方法上是否加了某注解,后者判断获取到的值是哪值)

introductions
advice 传参 泛型的应用
切面实例化模型
给advice传递参数
通知参数名称——argNames属性, 参数为 JoinPoint、ProceedingJoinPoint、JoinPointStaticPart时,可以忽略argNames属性
给通知传递参数
Aop 小结
advice参数名称
advice的参数及泛型
给advice传递参数
给advice传递参数
切面实例化模型