AOP介绍
AOP是OOP的延续
意图:aop可以把与业务逻辑无关的,系统中公用的部分提取出来,让代码更干净。比如做事务处理,日志管理。使开发人员可以专心于核心业务的开发。提高效率。
主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等
主要的意图是:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立的指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。使开发人员在编写也为逻辑的时候可以专心核心业务。
举个例子:“不管返回给用户的消息是什么样的,我都要在前面加上‘尊敬的用户,’”,此时,我们可以拦截返回消息的get方法,在return的message前加上‘尊敬的用户,’。
AOP术语
Aspect (切面)
切面通常是指封装的用于横向插入系统的(事务、日志等)类比如:事务处理和日志处理可以理解为两个切面。
通知和切入点共同组成了切面:事件、地点和要发生的“故事”
JoinPoint(连接点)
程序执行过程中特定的点,实际上是对象的一个操作,如方法的调用或异常的抛出。可以说连接点就是指方法的调用。SpringAOP中,连接点就是指方法的调用理解:
连接点是虚的,可简单理解为切入点的集合一个切点一般匹配多个连接点
他只是对应程序所有需要进行插入切面的某个特定的位置的统称。每个切入点都应对应具体的连接点,spring运行期间就是根据这些切入点的定义将通知或拦截器插入到具体的连接点上。
连接点是程序执行的某个特定的点。Spring仅支持方法的连接点,即仅能在方法调用前,方法调用后,方法的调用前后、方法抛出异常时,在这些程序织入增强。SpringAOP中,连接点就是指方法的调用。
简单理解就是,切入点定义了需要插入切面的位置,类或方法;而连接点就是,在这个类或方法执行前,执行后,或执行前后,或抛出异常时(不同时间点),所需要调用增强放方法的点(其实就是对象的一个操作)的集合。SpringAOP中,连接点就是指方法的调用。
PointCut(切入点)
通常,切入点指的是类或者方法名,切面与程序的交叉点,即那些需要处理的连接点。如某个通知要应用得到所有以add开头的方法中,那么所有满足的方法都是切入点。
切面表达式
pointcut="execution(* com.lx.service.*.*(..))"第一个 * 通配 任意返回值类型
第二个 * 通配 任意类
第三个 * 通配 任意方法
两个点 通配 任意数量的参数
切点和连接点不是一对一的关系,一个切点一般匹配多个连接点.
通知定义了切面发生的“故事”和时机,那么切入点就定义了“故事”发生的地点。
Advice(通知/增强)
在切面的某个特定的连接点(Joinpoint)上执行的方法。通知是切面的一种功能的具体实现,可以完成简单的织入功能。Target Object(目标对象)
目标对象指将要被增强的对象,如果aop框架采用的是动态的aop实现,那么该对象就是一个被代理的对象
Proxy(代理)
将通知应用到目标对象之后,被动态创建的对象Weaving(织入)
织入是指将切面代码插入到目标对象上,从而生成代理对象的过程。代理的invoke方法完成的工作,可以称为织入。Advisor(通知器/增强器)
Advisor是切面的另一种实现,定义应该使用哪个通知advice并在哪个切入点pointcut使用它,也就是通过advisor把advice和pointcut结合起来。Advisor多用于事务管理
Aspect多用于日志,缓存
动态代理
aop中的代理,由aop框架动态生成的一个对象,该对象可以作为目标对象来使用。
一般不要在别人的代码上修改。
Spring中的aop代理:jdk动态代理和cglib代理.
Jdk动态代理
Jdk动态代理类是通过java.lang.reflect.Proxy类来实现的,我们可以调用Proxy类的newProxyInstance()方法来创建代理对象,对于使用业务实现接口的类Spring默认会jdk动态代理来实现aop使用jdk动态代理的对象必须实现一个或多个接口
package com.lx.jdk; public interface UserDao { public void addUser(); public void deleteUser(); } |
package com.lx.jdk; //target类 public class UserDaoImpl implements UserDao { @Override public void addUser() { // TODO Auto-generated method stub System.out.println("添加用户"); } @Override public void deleteUser() { // TODO Auto-generated method stub System.out.println("删除用户"); } } |
package com.lx.aspect; //切面类:多个通知advice public class MyAspect { public void check_Permissions() { System.out.println("模拟检查权限..."); } public void log() { System.out.println("模拟记录日志..."); } } |
package com.lx.jdk; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import com.lx.aspect.MyAspect; /** * jdk代理类 * @author Administrator * */ public class JdkProxy implements InvocationHandler { //声明目标类接口 private UserDao userDao; //创建代理方法 public Object createProxy(UserDao userDao) { this.userDao=userDao; //1类加载器 ClassLoader classloader=JdkProxy.class.getClassLoader(); //2被代理对象实现的所有接口 Class[] clazz=userDao.getClass().getInterfaces(); //3使用代理类,进行增强,返回的是代理后的对象 return Proxy.newProxyInstance(classloader, clazz, this); } /** * 所有动态代理类的方法调用,都会交给invoke()方法去处理 * proxy被代理后的对象 * method将要执行的方法信息(反射) * args执行方法是需要的参数 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub //声明切面 MyAspect myAspect=new MyAspect(); //前增强 myAspect.check_Permissions(); //在目标上调用方法,并传入参数 Object obj=method.invoke(userDao, args); //后增强 myAspect.log(); return obj; } } |
package com.lx.jdk; //测试 public class jdkTest { public static void main(String[] args) { // TODO Auto-generated method stub //创建代理对象 JdkProxy jdkProxy=new JdkProxy(); //创建目标对象 UserDao userDao=new UserDaoImpl(); //从代理的对象中获取增强后的目标对象 UserDao userDao1=(UserDao) jdkProxy.createProxy(userDao); //执行方法 userDao1.addUser(); userDao1.deleteUser(); } } |
CGLIB(code generation libray)代理
CGLIB(code generation libray)是一个高性能开源的代码生成包
如果没有实现接口的类进行代理,那就可用Cglib来代理
Spring已集成Cglib所要的包,无需导入jar
package com.lx.jdk; //target类 public class UserDao{ public void addUser() { // TODO Auto-generated method stub System.out.println("添加用户"); } public void deleteUser() { // TODO Auto-generated method stub System.out.println("删除用户"); } } |
package com.lx.aspect; //切面类:多个通知advice public class MyAspect { public void check_Permissions() { System.out.println("模拟检查权限..."); } public void log() { System.out.println("模拟记录日志..."); } } |
package com.lx.cglib; import java.lang.reflect.Method; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import com.lx.aspect.MyAspect; //Cglib代理类 public class CglibProxy implements MethodInterceptor { //代理方法 public Object createProxy(Object target) { //创建一个动态类对象 Enhancer enhancer=new Enhancer(); //确定需要增强的类,设置其父类 enhancer.setSuperclass(target.getClass()); //添加回调函数 enhancer.setCallback(this); //返回创建爱你的代理类 return enhancer.create(); } /** * Proxy Cglib根据指定父类生成的代理对象 * method拦截的方法 * args拦截方法的参数数组 * methodProxy 方法的代理对象,用于执行父类方法 */ @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { // TODO Auto-generated method stub //创建切面类对象 MyAspect myAspect=new MyAspect(); //前增强 myAspect.check_Permissions(); //目标方法执行 Object obj=methodProxy.invokeSuper(proxy, args); //后增强 myAspect.log(); return obj; } } |
package com.lx.cglib; //测试 public class CglibTest { public static void main(String[] args) { // TODO Auto-generated method stub //创建代理对象 CglibProxy cglibProxy=new CglibProxy(); //创建目标对象 UserDao userDao=new UserDao(); //获取增强增强后的目标对象 UserDao userDao1=(UserDao)cglibProxy.createProxy(userDao); //执行方法 userDao1.addUser(); userDao1.deleteUser(); } } |
基于代理类的aop实现
Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)
在spring中,使用Proxyfactorybean是创建aop代理的最基本方式。
通知名称 |
接口 |
前置通知 |
org.springframework.aop.MethodBeforeAdvice |
后置返回通知 |
org.springframework.aop.AfterReturningAdvice |
后置通知 |
org.springframework.aop.AfterAdvice |
环绕通知 |
org.springframework.aop.MethodInterceptor |
异常通知 |
org.springframework.aop.ThrowsAdvice |
引入通知 |
org.springframework.aop.IntroductionInterceptor |
Spring中通知按照在目标方法的连接点的位置分:
org.springframework.aop.MethodBeforeAdvice(前置通知)
在目标方法执行前实施增强,可应用权限管理等功能
org.aopalliance.intercept.MethodInterceptor(环绕通知)
环绕通知 集成了前置 后置 返回通知等功能
在目标方法执行前后实施增强,可应用于日志、事物管理等功能
org.springframework.aop.AfterAdvice(后置通知)
后置返回通知是在方法执行return后执行,这个是不可能可以修改方法的返回值的。
org.springframework.aop.AfterRetruningAdvice(后置返回通知)
后置通知是在方法返回前执行的,而且就算目标方法抛出异常,后置通知也会执行,但抛出异常时,后置返回通知并不会执行
在目标方法执行后实施增强,可用于关闭流、上传文件、删除临时文件等
org.springframework.aop.ThrowsAdvice(异常通知)
在方法抛出异常后实施增强,可应用于处理异常记录日志等功能
org.springframework.aop.IntroductionInterceptor(引介通知)
在目标类中添加一些新的方法和属性,可应用于修改老版本程序(增强类)
前置,环绕,后置通知,后置返回通知,异常,引入通知。
后置返回和后置通知
后置返回通知是在方法执行return后执行,这个是不可能可以修改方法的返回值的。
后置通知是在方法返回前执行的,而且就算目标方法抛出异常,后置通知也会执行,但抛出异常时,后置返回通知并不会执行。
ProxyFactoryBean
ProxyFactoryBean是FactoryBean接口实现类,FactoryBean负责实例化一个bean,而ProxyFactoryBean负责为其他bean创建代理实例。Spring中ProxyFactoryBean是创建aop 代理最基本方式。
属性名称 |
描述 |
Target |
代理目标对象 |
ProxyInterface |
代理要实现的接口,多个的话<list><value></value>…..</list> |
proxyTargetClass |
是否对类代理而不是接口,true使用cglib |
interceptorNames |
需要织入目标的advice |
Singleton |
返回代理是否为单实例,默认为true |
optimize |
设置为true,强制cglib |
demo
package com.lx.factorybean; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; //切面类:环绕通知为例 public class MyAspect implements MethodInterceptor{ @Override public Object invoke(MethodInvocation arg0) throws Throwable { // TODO Auto-generated method stub check_Permissions(); //执行目标方法 Object obj=arg0.proceed(); log(); return obj; } //֪ private void log() { // TODO Auto-generated method stub System.out.println("模拟检查权限..."); } private void check_Permissions() { // TODO Auto-generated method stub System.out.println("模拟记录日志..."); } } |
<?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-4.3.xsd">
<!-- 1 目标类 --> <bean id="userDao"
class="com.lx.jdk.UserDaoImpl"></bean> <!-- 2 切面类 --> <bean id="myAspect"
class="com.lx.factorybean.MyAspect"></bean> <!-- 3 使用Spring代理工厂定义一个名称为userDaoProxy的代理对象 --> <bean id="userDaoProxy"
class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 3.1 指定代理实现的接口 --> <property name="proxyInterfaces"
value="com.lx.jdk.UserDao"></property> <!-- 3.2指定目标对象 --> <property name="target"
ref="userDao"></property> <!-- 3.3指定切面,织入环绕通知 --> <property name="interceptorNames"
value="myAspect"></property> <!-- 3.4指定代理方式,true使用cglib,false默认jdk动态代理 --> <property name="proxyTargetClass"
value="true"></property> </bean> </beans> |
package com.lx.factorybean; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.lx.jdk.UserDao; public class PropertyFactoryBeanTest { public static void main(String[] args) { // TODO Auto-generated method stub String xmlpath="com/lx/factorybean/applicationContext.xml"; ApplicationContext applicationContext=new ClassPathXmlApplicationContext(xmlpath); UserDao userDao=(UserDao)applicationContext.getBean("userDaoProxy"); userDao.addUser(); userDao.deleteUser(); } } |
ClassPathXmlApplicationContext
默认文件路径是src下那一级
FileSystemXmlApplicationContext
这个类,默认获取的是项目路径,默认文件路径是项目名下一级,与src同级。
FactoryBean和BeanFactory区别
BeanFactory和FactoryBean其实没有什么比较性的,只是两者的名称特别接近!面试一般会问
BeanFactory和FactoryBean都是Spring中定义的接口
BeanFactory 是ioc容器的底层实现接口,是ApplicationContext 顶级接口,spring不允许我们直接操作 BeanFactory bean工厂,所以为我们提供了ApplicationContext 这个接口 此接口集成BeanFactory 接口,ApplicationContext包含BeanFactory的所有功能,同时还进行更多的扩展。
FactoryBean 是spirng提供的工厂bean的一个接口
FactoryBean表示的实际上就是一个bean对象,大多数情况下我们只会让spring帮助我们生成一个bean对象,但我们并不关心对象生成的具体过程。但是通过实现这个接口可以使我们实现自己的bean对象生成过程并且放入到spring容器中,这个接口的定义就是满足这样一个需求场景。
接口中只有三个方法,一般我们会重写getObject方法,来实现对象的注入。
public interface
FactoryBean<T> { @Nullable T getObject() throws Exception; @Nullable Class<?> getObjectType(); default boolean isSingleton() { return true; } } |
@Component public class
MyFactoryBean implements FactoryBean { @Override public Object getObject() throws
Exception { return " MyFactory"; } @Override public Class<?> getObjectType() { return null; } } |
public class
TestSpring { public static void main(String[] args) {
AnnotationConfigApplicationContext
applicationContext = new AnnotationConfigApplicationContext("com.lx
.spring"); //返回的是getObject中定义的方法返回值。 Object o1 =
applicationContext.getBean("myFactoryBean"); //前面加一个&符合就可以拿到MyFactoryBean本身对象的实例 Object o2 =
applicationContext.getBean("&myFactoryBean"); System.out.println(o1); // MyFactory System.out.println(o2);
//com.lx.spring. FactoryBean@18c2bdwc } } |