概念:经过代理类为原始类(目标类)增长额外功能java
好处:利于原始类(目标类)的维护
从这点看和静态代理同样同样的spring
<!--Spring aop支持--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.1.14.RELEASE</version> </dependency> <!--aspectj框架包--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.8.8</version> </dependency> <!--编制切面包--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.3</version> </dependency>
package proxy.service.impl; import proxy.service.UserService; public class UserServiceImpl implements UserService { @Override public void login(String username, String password) { System.out.println("UserServiceImpl.login 我是【service核心】"); } }
2. 方法前置加强代码,须要实现MethodBeforeAdvice接口express
package proxy.service.aop;import org.springframework.aop.BeforeAdvice;import org.springframework.aop.MethodBeforeAdvice;import java.lang.reflect.Method;/** * @Classname MyAdvice * @Description 实现spring aop 包下MethodBeforeAdvice接口添加前置通知 这样只要在切面上的方法在执行前 * 均要加强: MyBefore.before 【service外围】 */
publicclass MyBefore implements MethodBeforeAdvice{/** * * @param method 目标方法 login() * @param args login()的参数username\password * @param target 目标对象userServiceImpl * @throws Throwable */@Override
public void before(Method method, Object[] args, Object target)throws Throwable { System.out.println("MyBefore.before 【service外围】");
}
}
3.配置文件编程
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--目标类 此时和代理类无关--> <bean id = "userService" class="proxy.service.impl.UserServiceImpl"/> <bean id = "orderService" class="proxy.service.impl.OrderServiceImpl"/> <!--通知类--> <bean id= "myBefore" class="proxy.service.aop.MyBefore"/> <!--aop配置标签,会自动添加工做空间--> <aop:config> <!--定义接入点,即那些方法须要被增强--> <aop:pointcut id="pointcut" expression="execution(* *(..))"/> <!--切入点和通知的结合--> <aop:advisor advice-ref="myBefore" pointcut-ref="pointcut"/> </aop:config> </beans>
4. 测试app
/** * spring动态代理 */ @Test public void test3() { ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext6.xml"); OrderService userService = (OrderService)ctx.getBean("orderService"); userService.order(); }
5.debug查看得到的确实是代理类框架
1.Spring工厂经过原始对象的id值获取的是代理对象jvm
2.获取代理对象后,能够经过声明接口类型,进行对象的存储。ide
动态代理和以前的静态代理不一样,他不须要java文件而后经过类加载子系统,加载进运行时数据区,这里是直接使用字节码相关技术,在JVM内存中直接生成当前类的代理类。也就没有那么多的java类让咱们去管理,也就解决了这个痛点。另外他实用配置的方式对全部须要加强的类进行切入点的统一配置,这样就没有了代码冗余。函数
那么确定会有人提出问题,这个只能对方法的前置进行加强太鸡肋了。有没有更好的办法,能够在以前和以后均能加强呢?测试
您能想到的问题spring都想到了。接着往下看。使用MethodInterceptor这个就能够实现。 特别注意是这个报下的:import org.aopalliance.intercept.MethodInterceptor;spring使用了aop联盟的相关接口来处理这个问题,并非原生的spring的解决方案。
package proxy.service.aop; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class MyArround implements MethodInterceptor { /** * * @param invocation 和MethodBeforeAdvice.before()方法中的Method参数同样,只是更为强大的封装 * @return Object 原始方法的返回值 * @throws Throwable */ @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("MyArround.invoke 【service外围】前面的"); Object res = null; try {//统一对异常进行抛出 res = invocation.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } System.out.println("MyArround.invoke 【service外围】后面的"); return res; //return false; //影响原始方法的返回值。 } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--目标类 此时和代理类无关--> <bean id = "userService" class="proxy.service.impl.UserServiceImpl"/> <bean id = "orderService" class="proxy.service.impl.OrderServiceImpl"/> <!--通知类--> <bean id= "myBefore" class="proxy.service.aop.MyBefore"/> <!--aop配置标签,会自动添加工做空间--> <aop:config> <!--定义接入点,即那些方法须要被增强--> <aop:pointcut id="pointcut" expression="execution(* *(..))"/> <!--切入点和通知的结合--> <aop:advisor advice-ref="myBefore" pointcut-ref="pointcut"/> </aop:config> </beans>
<aop:pointcut id=“pointcut” expression=“execution(* *(…))”/>表示对因此方法进行加强。
方法切入点表达式
execution(* *(…)) :因此方法进行加强
* login(…) :login方法进行加强
* login(String,String):login 方法且两个参数为String的方法加强
* register(proxy.service.User) register方法且参数为User加强
类切入点
* proxy.service.impl.UserServiceImpl.*(…) 类中的全部方法加入了加强功能
* .UserServiceImpl.(…) 类只在一级包,对类全部方法加强
* …UserServiceImpl.(…) 类存在多级包,UserServiceImpl类下的全部方法加强
包切入点表达式
\ * proxy.service.impl..(…) 切入点包中的全部类,必须在impl中,不能在impl包的子包中
* proxy.service.impl….(…)
execution
能够知足你的全部想象,能够作全部的事情:方法切入、类切入、包切入
args
execution(* *(String,String)) 等价于:args(String,String)
within 和args互补
主要用于进行类、包切入点表达式的匹配
execution(* …UserServiceImpl.(…))等价于within(…UserServiceImpl)
execution( com.baizhiedu.proxy….(…))等价于ithin(com.baizhiedu.proxy…*)
@annotation
package proxy; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @Classname Log * @Description 用于切面 */ @Target(ElementType.METHOD) //使用在方法上 @Retention(RetentionPolicy.RUNTIME) //使用在运行时环境中 public @interface Log { }
使用时:<aop:pointcut id="" expression="@annotation(proxy.Log)"/>
面向切面编程的步骤:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--目标类 此时和代理类无关--> <bean id = "userService" class="proxy.service.impl.UserServiceImpl"/> <bean id = "orderService" class="proxy.service.impl.OrderServiceImpl"/> <!--通知类--> <!-- <bean id= "myBefore" class="proxy.service.aop.MyBefore"/>--> <bean id="myArround" class="proxy.service.aop.MyArround"/> <!--aop配置标签,会自动添加工做空间--> <aop:config> <!--定义接入点,即那些方法须要被增强--> <aop:pointcut id="pointcut" expression="execution(* *(..))"/> <!--切入点和通知的结合--> <aop:advisor advice-ref="myArround" pointcut-ref="pointcut"/> </aop:config> </beans>
测试:
@Test public void test4() { ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext6.xml"); UserService userService = (UserService)ctx.getBean("userService"); userService.login("zhangsan","111111"); userService.regester(new User(2, "222222")); }
测试结果
MyArround.invoke 【service外围】前面的
UserServiceImpl.login 我是【service核心】
MyArround.invoke 【service外围】后面的
MyArround.invoke 【service外围】前面的
UserServiceImpl.regester 我是【service核心】
MyArround.invoke 【service外围】后面的
动态字节码技术原理:
动态代理细节分析:
1.spring建立的动态代理类在哪里呢?
spring框架在运行的时,经过动态字节码技术,在jvm中建立的,运行在jvm内部, 等程序结束后会和jvm类一块儿消失。
2.什么叫动态字节码技术?
Java运行一个类, 其实就是运行这个类的编译后的字节码--->object
java在类加载的时候会把字节码加载到jvm的内存中。
3.那么问题来了,动态字节码的字节码从哪里来的呢?
(动态字节码其实就是不须要(.Java文件生成.class文件的过程)字节码的一个过程),它是由一些第三方动态字节码框架(如ASM,Javassist,cglib)直接在jvm中生成字节码(动态字节码),当jvm结束,动态字节码也跟着消失了。
结论:动态代理不须要定义类文件,都是在jvm中自动建立的,因此不会有静态代理,类文件数量过多,影响项目管理的问题。