1:AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)
2:Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式向目标类织入增强代码
3:AOP相关术语
Joinpoint(连接点):所谓连接点是指那些被拦截到的点,在spring中,这些点指的是方法,因为spring只支持方法类型的连接点;
Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义;
Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知,通知分为前置通知、后置通知、异常通知、最终通知、环绕通知(切面要完成的功能);
Introduction(引介):引介是一种特殊的通知,在不修改类代码的前提下,可以在运行期间为类动态地添加一些方法或field;
Target(目标对象):代理的目标对象;
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程,Spring采用动态代理织入;
Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类;
Aspect(切面):是切入点和通知的结合。
4:JDK动态代理(只能对实现接口的类进行代理)
4.1UserDao
public interface UserDao {
public void save();
public void update();
public void delete();
public void find();
}
4.2UserDaoImpl
public class UserDaoImpl implements UserDao{
public void save() {
System.out.println("UserDao保存用户");
}
public void update() {
System.out.println("UserDao编辑用户");
}
public void delete() {
System.out.println("UserDao删除用户");
}
public void find() {
System.out.println("UserDao查询用户");
}
}
4.3MyJdkProxy
public class MyJdkProxy implements InvocationHandler {
private UserDao userDao;
//构造方法
public MyJdkProxy(UserDao userDao){
this.userDao = userDao;
}
//JDK动态代理,可以对实现接口的类产生动态代理
//创建userdeo的代理类
public Object createProxy(){
/*
参数一:类加载器
参数二:实现的接口
参数三:invocationhandler的接口
*/
Object proxy = Proxy.newProxyInstance(userDao.getClass().getClassLoader(),userDao.getClass().getInterfaces(),this);
return proxy;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//判断是否是save方法
if("save".equals(method.getName())){
System.out.println("权限校验");
return method.invoke(userDao,args);
}
return method.invoke(userDao,args);
}
}
4.4SpringDemo1
public class SpringDemo1 {
@Test
public void demo1(){
UserDao userDao = new UserDaoImpl();
//产生代理对象
UserDao proxy = (UserDao) new MyJdkProxy(userDao).createProxy();
proxy.save();
proxy.update();
proxy.delete();
proxy.find();
}
}
5:CGLIB生成代理(采用非常底层字节码技术,为一个类创建子类,解决无接口代理问题)
5.1ProductDao
public class ProductDao {
public void save(){
System.out.println("保存商品");
}
public void update(){
System.out.println("更新商品");
}
public void delete(){
System.out.println("删除商品");
}
public void find(){
System.out.println("查找商品");
}
}
5.2MyCglibProxy
public class MyCglibProxy implements MethodInterceptor {
private ProductDao productDao;
public MyCglibProxy (ProductDao productDao){
this.productDao = productDao;
}
public Object createProxy(){
//创建核心类
Enhancer enhancer = new Enhancer();
//设置父类,即目标类为productDao
enhancer.setSuperclass(productDao.getClass());
//设置回调
enhancer.setCallback(this);
//生成代理
Object proxy = enhancer.create();
return proxy;
}
//methodProxy即方法代理
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if("save".equals(method.getName())){
System.out.println("权限校验");
return methodProxy.invokeSuper(proxy,args);
}
//invokeSuper即调用父类的方法,即ProductDao里面的,即不做改变
return methodProxy.invokeSuper(proxy,args);
}
}
5.3SpringDemo2
public class SpringDemo2 {
@Test
public void demo2(){
ProductDao productDao = new ProductDao();
//产生代理类
ProductDao proxy = (ProductDao) new MyCglibProxy(productDao).createProxy();
proxy.save();
proxy.update();
proxy.delete();
proxy.find();
}
}
6:代理知识总结
6.1Spring在运行期间,生成动态代理对象,不需要特殊的编译器
6.2Spring AOP的底层就是通过JDK动态代理或CGLib动态代理技术,为目标Bean执行横向织入,若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理,若目标对象没有实现任何接口,spring使用CGLib库生成目标对象的子类
6.3程序中应优先对接口创建代理,便于程序解耦为何
6.4标记为final的方法,不能为代理,因为无法进行覆盖
6.5只支持方法连接点,不提供属性连接点,允许对方法增强
7:Spring AOP增强类型
7.1AOP联盟为通知Advice定义了org.aopalliance.aop.Interface.Advice
Spring按照通知Advice在目标类方法的连接点位置,可以分为5类:
-前置通知,在目标方法执行前实施增强
-后置通知,在目标方法执行后实施增强
-环绕通知,在目标方法执行前后实施增强
-异常通知,在方法抛出异常后实施增强
-引介通知,在目标类中添加一些新的方法和属性(Spring不支持)
7.2Spring AOP切面类型
-Advisor,代表一般切面,Advice本身就是一个切面,对目标类所有方法进行拦截
-PointcutAdvisor,代表具有切点的切面,可以指定栏目目标类哪些方法
8:Advisor
8.1 pom.xml引入响应的包
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
8.2 resources目录下创建applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置目标类-->
<bean id="studentDao" class="com.imooc.aop.demo3.StudentDaoImpl"/>
<!--这里是前置通知-->
<bean id="myBeforeAdvice" class="com.imooc.aop.demo3.MyBeforeAdvice"/>
<!--String的AOP产生代理对象-->
<bean id="studentDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--配置目标类-->
<property name="target" ref="studentDao"/>
<!--配置目标类所实现的接口-->
<property name="proxyInterfaces" value="com.imooc.aop.demo3.StudentDao"/>
<!--增强类型-->
<property name="interceptorNames" value="myBeforeAdvice"/>
</bean>
</beans>
8.3 StudentDao
public interface StudentDao {
public void find();
public void save();
public void update();
public void delete();
}
8.4 StudentDaoImpl
public class StudentDaoImpl implements StudentDao {
public void find() {
System.out.println("查询学生");
}
public void save() {
System.out.println("保存学生");
}
public void update() {
System.out.println("编辑学生");
}
public void delete() {
System.out.println("删除学生");
}
}
8.5 MyBeforeAdvice
public class MyBeforeAdvice implements MethodBeforeAdvice {
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("-这是前置增强-");
}
}
8.6 测试
@RunWith(SpringJUnit4ClassRunner.class) //使用junit4进行测试
@ContextConfiguration("classpath:applicationContext.xml") //加载配置文件
public class SpringDemo3 {
// @Resource(name="studentDao")
@Resource(name="studentDaoProxy")
private StudentDao studentDao;
@Test
public void demo1(){
studentDao.find();
studentDao.update();
studentDao.save();
studentDao.delete();
}
}
8.7一般切面的其他属性配置
-interceptorNames:需要织入目标的Advice
-singleton:返回代理是否为单例,默认为单例
-optimize:当设置为true,强制使用CGLib
9 PointcutAdvisor切点切面
-使用普通Advice作为切面,将对目标类所有方法进行拦截,不够灵活,实际开发中常采用带有切点的切面
-常用PointcutAdvisor实现类,DefaultPointcutAdvusor最常用的切面类型,它可以通过任意Pointcut和Advice组合定义切面;JdkRegexpMethodPointcut构造正则表达式切点
9.1 applicationContext2.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置目标类-->
<bean id="customerDao" class="com.imooc.aop.demo4.CustomerDao"/>
<!--配置通知-->
<bean id="myAroundAdvice" class="com.imooc.aop.demo4.MyAroundAdvice"/>
<!--一般的切面是使用通知作为切面的,因为要对目标类的某个方法进行增强需要配置一个带有切入点的切面-->
<bean id="myAdvice" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!--pattern中配置正则表达式,.是任意字符,*是任意次数-->
<!--一个表达式的时候-->
<!--<property name="pattern" value=".*"/>-->
<!--多个表达式的时候-->
<property name="patterns" value=".*save,.*delete"/>
<property name="advice" ref="myAroundAdvice"/>
</bean>
<!--配置产生代理-->
<bean id="customerProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--配置target-->
<property name="target" ref="customerDao"/>
<property name="proxyTargetClass" value="true"/>
<property name="interceptorNames" value="myAdvice"/>
</bean>
</beans>
9.2 CustomerDao
public class CustomerDao {
public void find(){
System.out.println("查询");
}
public void save(){
System.out.println("保存");
}
public void update(){
System.out.println("编辑");
}
public void delete(){
System.out.println("删除");
}
}
9.3 MyAroundAdvice
public class MyAroundAdvice implements MethodInterceptor {
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("环绕前通知");
//执行目标方法
Object object = methodInvocation.proceed();
System.out.println("环绕后通知");
return object;
}
}
9.4 SpringDemo4
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class SpringDemo4 {
//@Resource(name="customerDao")
//注入代理对象
@Resource(name="customerProxy")
private CustomerDao customerDao;
@Test
public void demo1(){
//没有实现接口,使用CGLib
customerDao.find();
customerDao.delete();
customerDao.save();
customerDao.update();
}
}
10 自动创建代理
-前面代理中,每个代理都是通过ProxyFactoryBean织入切面代理,在实际开发中,非常多Bean每个都配置ProxyFactoryBean开发维护量巨大;
-BeanNameAutoProxyCreator根据Bean名称创建代理
-DefaultAdvisorAutoProxyCreator根据Advisor本身包含信息创建代理
-AnnotationAwareAspectJAutoProxyCreator基于Bean中的AspectJ注解进行自动代理
10.1 BeanNameAutoProxyCreator举例
-对所有以Dao结尾Bean所有方法使用代理
10.1.1 applicationContext3.xml
<!--配置目标类-->
<bean id="studentDao" class="com.imooc.aop.demo5.StudentDaoImpl"/>
<bean id="customerDao" class="com.imooc.aop.demo5.CustomerDao"/>
<!--配置增强-->
<bean id="myBeforeAdvice" class="com.imooc.aop.demo5.MyBeforeAdvice"/>
<bean id="myAroundAdivce" class="com.imooc.aop.demo5.MyAroundAdvice"/>
<!--配置自动代理-->
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="*Dao"/>
<property name="interceptorNames" value="myBeforeAdvice"/>
</bean>
10.1.2 SpringDemo5
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext3.xml")
public class SpringDemo5 {
@Resource(name="studentDao")
private StudentDao studentDao;
@Resource(name="customerDao")
private CustomerDao customerDao;
@Test
public void demo1(){
//查看结果发现,不能针对单个save方法去增强
studentDao.find();
studentDao.delete();
studentDao.save();
studentDao.update();
customerDao.find();
customerDao.delete();
customerDao.save();
customerDao.update();
}
}
10.2 DefaultAdvisorAutoProxyCreator举例
10.2.1 applicationContext4.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置目标类-->
<bean id="studentDao" class="com.imooc.aop.demo6.StudentDaoImpl"/>
<bean id="customerDao" class="com.imooc.aop.demo6.CustomerDao"/>
<!--配置增强-->
<bean id="myBeforeAdvice" class="com.imooc.aop.demo6.MyBeforeAdvice"/>
<bean id="myAroundAdivce" class="com.imooc.aop.demo6.MyAroundAdvice"/>
<!--配置切面-->
<bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="pattern" value="com\.imooc\.aop\.demo6\.CustomerDao\.save"/>
<property name="advice" ref="myAroundAdivce"/>
</bean>
<!--配置自动代理-->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
</bean>
</beans>
10.2.2 SpringDemo6
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext4.xml")
public class SpringDemo6 {
@Resource(name="studentDao")
private StudentDao studentDao;
@Resource(name="customerDao")
private CustomerDao customerDao;
@Test
public void demo1(){
//查看结果发现,不能针对单个save方法去增强
studentDao.find();
studentDao.delete();
studentDao.save();
studentDao.update();
customerDao.find();
customerDao.delete();
customerDao.save();
customerDao.update();
}
}