文章目录
- Java反射
- 回顾
- AOP
- 代理模式
- AOP概念及术语
- 概述
- 术语
- 作用
- 基于注解的AOP
- 步骤
- 依赖
- 配置文件
- 切入点表达式语法
- 切面类
- 重用切入点表达式
- 切面的优先级
- 基于XML的AOP
- 单元测试JUnit
- 引入依赖
- JUnit5
Java反射
Spring框架的IoC基于java反射机制实现,反射是指在运行状态中,对任意类都能知道其所有属性和方法;对任何对象都能调用其所有方法和属性。即程序在运行时能够获取自身信息。
剖析一个类则需获取到该类的class对象,使用到java.lang.class和java.lang.relectAPI,Class对象是反射的根源。
回顾
- 首先创建类和测试类
package com.jobs.reflect;public class Car {private String name;private String color;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}public Car(String name, String color) {this.name = name;this.color = color;}public Car() {}private void drive(){System.out.println("drive");}
}
- 获取Class对象的多种方法
public class CarTest {public carTest1() throws Exception {//1 类名.classClass aClass1 = Car.class;//2 对象.getClass()Class aClass2 = new Car().getClass();//3 Class.forName("全路径")Class aClass3 = Class.forName("com.jobs.reflect.Car");}
}
- 获取构造方法
public void carTest02() throws Exception{//获取所有构造//getConstructors()获取public//getDeclaredConstructors()获取所有(public、private)Class aclass = Car.class;Constructor[] constructors = aclass.getConstructors();for (Constructor c :constructors) {System.out.println(c.getName() + " " + c.getParameterCount());}//指定有参构造创建对象//publicConstructor c1 = aclass.getConstructor(String.class, String.class);Car car1 = (Car) c1.newInstance("小米","blue");//privateConstructor c2 = aclass.getDeclaredConstructor(String.class, String.class);//必须先允许访问才可更改c2.setAccessible(true);Car car2 = (Car) c2.newInstance("保时捷","red");
}
- 获取属性及向属性赋值
@Test
public void carTest03() throws Exception {Class aclass = Car.class;Car car = (Car) aclass.getDeclaredConstructor().newInstance();//获取所有属性//getFields() ---- public//getDeclaredFields() ---- 所有Field[] fields = aclass.getFields();Field[] fieldss = aclass.getDeclaredFields();for (Field field :fieldss) {if (field.getName().equals("name")) {//必须先允许访问才可更改field.setAccessible(true);field.set(car, "xiaomi");}if (field.getName().equals("color")) {field.setAccessible(true);field.set(car, "blue");}System.out.println(car);}
}
- 获取方法
public void carTest04() throws Exception {Car car = new Car("xiaomi", "blue");Class aclass = car.getClass();//getMethods() ---- pulic//getDeclaredMethods() --- allMethod[] methods = aclass.getMethods();for (Method m :methods) {if (m.getName().equals("toString")) {String invoke = (String) m.invoke(car);System.out.println(invoke);}}Method[] methodss = aclass.getDeclaredMethods();for (Method m :methodss) {if (m.getName().equals("drive")) {//先许可m.setAccessible(true);m.invoke(car);}}
}
AOP
代理模式
- 属于结构型模式的设计模式,作用为通过一个代理类,使得在调用目标方法时,不再直接对目标方法进行调用,而是通过代理类进行间接调用。让不属于目标方法核心逻辑的内容从中剥离,达成解耦。
- 调用目标方法时先调用代理对象的方法,减少对目标方法的调用和打扰,同时让附加功能能够集中在一起,便于统一维护。
代理:将非核心逻辑剥离后,封装这些非核心逻辑的类、对象、方法
目标:被代理”套用了“非核心逻辑代码的类、对象、方法
- 静态代理
创建代理类,在代理类中创建目标对象,非核心逻辑代码外调用目标方法;
虽然实现了解耦,但代码已经固定,因此不具备灵活性;
public class CalculatorStaticProxy implements Calculator{//传入目标对象private Calculator calculator;public CalculatorStaticProxy(Calculator calculator){this.calculator = calculator;}@Overridepublic int add(int a, int b){System.out.println("[日志]");//调用目标方法int result = calculator.add(a, b);System.out.println("[日志]");return result;
}
- 动态代理
将非核心逻辑功能集中到代理类中,将来有任何此功能需求,都能通过该代理类实现。
代理类
public class ProxyFactory {//目标对象private Object target;public ProxyFactory(Object target){this.target = target;}//返回代理对象public Object getProxy(){/** Proxy.newProxyInstance()有三个参数* 1 ClassLoader:加载动态生成代理类的加载器* 2 Class[] interfaces:目录对象实现的所有接口的class类型数组* 3 InvocationHandler:设置代理对象实现目标对象方法的过程* *///1 ClassLoaderClassLoader classLoader = target.getClass().getClassLoader();//2 Class[] interfacesClass<?>[] interfaces = target.getClass().getInterfaces();//3 InvocationHandlerInvocationHandler invocationHandler = new InvocationHandler(){//1 proxy:代理对象//2 method:需要重写目标对象的方法//3 args:method方法所需参数@Overridepublic Object invoke(Object proxy,Method method,Object[] args) throws Throwable {//方法调用前System.out.println("[动态代理日志] "+method.getName()+", 参数" + Arrays.toString(args));//调用目标方法Object result = method.invoke(target, args);//方法调用后System.out.println("[动态代理日志] "+method.getName()+", 结果" + result);return result;}};return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);}
}
测试
public class CalculatorTest {public static void main(String[] args) {ProxyFactory proxyFactory = new ProxyFactory(new CalculatorImpl());Calculator proxy = (Calculator) proxyFactory.getProxy();proxy.add(1, 2);}
}
输出结果
[动态代理日志] add, 参数[1, 2]
[动态代理日志] add, 结果3
AOP概念及术语
概述
AOP(aspect oriented programming)是一种设计思想,面向切面编程,以预编译和运行期动态代理方式实现。在不修改源代码的情况下,给程序动态统一添加额外功能的一种技术。
利用AOP对业务逻辑各部分进行隔离,从而降低耦合,提高程序可重用性,提高开发效率。
术语
- 横切关注点
分散在各模块中解决同一问题,可抽取出来的同一类非核心业务;
此概念不是语法层面,而是根据附加功能逻辑层面需要; - 通知(增强)
每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法就叫通知方法;
前置通知:目标方法前执行;
返回通知:目标方法成功结束后执行
异常通知:目标方法异常结束后执行
后置通知:目标方法最终结束后执行
环绕通知:使用try—catch—finally围绕整个目标方法,包含上述四种通知位置
- 切面
封装通知方法的类
- 目标
被代理的目标对象 - 代理
代理对象 - 连接点
逻辑概念,方法中每个横切位置看作连接点,即Spring允许使用通知的地方 - 切入点
定位连接点的方式,Spring的AOP技术可以通过切入点定位到特定的连接点,即要增强的地方
eg. 连接点—数据库中的记录;切入点—查询记录的SQL语句
作用
- 简化代码:抽取固定位置重复代码,让目标方法专注核心功能;
- 代码增强:特定功能封装进切面类中,在有需要的地方套用,被套用了切面逻辑的方法就被切面增强;
基于注解的AOP
- 动态代理分为JDK方式和cglib方式;
- 当目标类有接口时使用JDK和cglib方式,没有接口时只能使用cglib方式;
- JDK动态代理生成代理类会在
com.sun.proxy
包下,类名为$proxy1
,和目标类实现相同接口; - cglib动态代理生成代理类会在目标相同包下,继承目标类;
- AspectJ:AOP思想的一种实现,本质是静态代理,其将代理逻辑”织入“目标类编译得到的字节码文件,所以最终效果是动态的。Spring只是借用AspectJ的注解。
步骤
- 引入AOP依赖
- 创建目标资源:接口;实现类;
- 创建切面类:切入点;通知类型;
依赖
<!--spring aop-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>6.0.2</version>
</dependency>
<!--spring aspects-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>6.0.2</version>
</dependency>
配置文件
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><!--组件扫描--><context:component-scan base-package="com.jobs.spring6.aop.annotation_aop"></context:component-scan><!--aspectj自动代理,并为目标对象生成代理--><aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</bean>
切入点表达式语法
切面类
设置切面类即可在目标对象上套用切面功能
@Aspect//切面类
@Component//IoC容器
public class LogAspect {//设置切入点和通知类型//切入点表达式://execution(访问修饰符 增强方法返回类型 增强方法所在类的全路径.方法名称(参数列表)//通知类型://前置@Befor(value="切入点表达式")@Before(value = "execution(public int com.jobs.spring6.aop.annotation_aop.CalculatorImpl.*(..))")public void beforeMethod(){System.out.println("before!");}//返回@AfterReturning//returning可以传递至方法内部,要求同名@AfterReturning(value = "execution(public int com.jobs.spring6.aop.annotation_aop.CalculatorImpl.*(..))",returning = "result")public void afterReturningMethod(Object result){System.out.println("afterreturning! result: " + result);}//异常@AfterThrowing//throwing可以传递错误信息至方法内部,要求同名@AfterThrowing(value = "execution(public int com.jobs.spring6.aop.annotation_aop.CalculatorImpl.*(..))",throwing = "exception")public void afterThrowingMethod(Throwable exception){System.out.println("afterthrowing! exception: " + exception);}//后置@After@After(value = "execution(public int com.jobs.spring6.aop.annotation_aop.CalculatorImpl.*(..))")public void afterMethod( ){System.out.println("after!");}//环绕@Around@Around(value = "execution(public int com.jobs.spring6.aop.annotation_aop.CalculatorImpl.*(..))")public Object afterReturningMethod(ProceedingJoinPoint joinPoint){Object result = null;try {System.out.println("around_before");//调用目标方法result = joinPoint.proceed();System.out.println("around_afterreturn");}catch (Throwable throwable){throwable.printStackTrace();System.out.println("around_afterthrow");}finally {System.out.println("around_after");}return result;}
}
重用切入点表达式
- 声明
//重用切入点表达式
@Pointcut(value = "execution(* com.jobs.spring6.aop.annotation_aop.CalculatorImpl.*(..))")
public void pointCut() { }
- 使用
//在同一个切面中,直接使用缩写
@After(value = "pointCut()")
//在不同切面中,添加缩写全路径
@After(value = "com.jobs.spring6.aop.annotation_aop.LogAspect.pointCut()")
切面的优先级
- 外层的切面 > 内层的切面
- 利用
@Order
注解可以控制优先级:
@Order(num1)…
@Order(num2)…
num1 > num2 则num2优先级高
基于XML的AOP
beanaop.xml
<!--配置aop通知类型-->
<aop:config><!--配置切面类--><aop:aspect ref="logAspect"><!--配置切入点--><aop:pointcut id="pointcut" expression="execution(*com.jobs.spring6.aop.xml_aop.CalculatorImpl.*(..))"/><!--配置通知--><!--before--><aop:before method="beforeMethod" pointcut-ref="pointcut"></aop:before><!--after--><aop:after method="afterMethod" pointcut-ref="pointcut"></aop:after><!--afterreturning--><aop:after-returning method="afterReturningMethod"returning="result"pointcut-ref="pointcut"></aop:after-returning><!--afterthrowing--><aop:after-throwing method="afterThrowingMethod"throwing="exception"pointcut-ref="pointcut"></aop:after-throwing><!--around--><aop:around method="aroundMethod" pointcut-ref="pointcut"></aop:around></aop:aspect>
</aop:config>
单元测试JUnit
减少固定任务
引入依赖
<!--spring整合junit依赖-->
<dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>6.0.2</version>
</dependency>
JUnit5
@SpringJUnitConfig(locations = "classpath:bean.xml")
/* or
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:bean.xml")
*/
public class SpringJUnitTest {//injection@Autowiredprivate User user;//Test@Testpublic void userTest(){System.out.println(user);user.run();}
}