文章目录
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 类名.class
Class 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());
}
//指定有参构造创建对象
//public
Constructor c1 = aclass.getConstructor(String.class, String.class);
Car car1 = (Car) c1.newInstance("小米","blue");
//private
Constructor 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() --- all
Method[] 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;
}
@Override
public 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 ClassLoader
ClassLoader classLoader = target.getClass().getClassLoader();
//2 Class[] interfaces
Class<?>[] interfaces = target.getClass().getInterfaces();
//3 InvocationHandler
InvocationHandler invocationHandler = new InvocationHandler(){
//1 proxy:代理对象
//2 method:需要重写目标对象的方法
//3 args:method方法所需参数
@Override
public 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/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
http://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
@Autowired
private User user;
//Test
@Test
public void userTest(){
System.out.println(user);
user.run();
}
}