Spring基础使用五
Spring基础使用五
基于注解的自动装配
@Autowired: 实现自动装配功能的注解
Autowired注解能够标识的位置
a>标识在成员变量上,此时不需要设置成员变量的set方法
b>标识在set方法上
c>为当前成员变量赋值的有参构造上
Autowired注解的原理
a>默认通过byType的方式,在IOC容器中通过类型匹配某个bean为属性赋值
b>若有多个类型匹配的bean,此时会自动转换为byName的方式实现自动装配的效果
即将要赋值的属性的属性名作为bean的id匹配某个bean为属性赋值
c>若byName和byType的方式都无法实现自动装配,即IOC容器中有多个类型匹配的bean
且这些bean的id和要赋值的属性的属性名都不一致,此时抛出异常: NoUniqueBeanDefinitionException
d>此时可以在要赋值的属性上,添加一个注解@Qualifier("value")
通过该注解的value属性值,指定某个bean的id,将这个bean为属性赋值
代理模式
概念:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
静态代理
接口:
package com.wfy.proxy;
public interface Calculator {
int add(int i , int j);
int sub(int i,int j);
int mul(int i , int j);
int div(int i , int j);
}
核心工程类:
package com.wfy.proxy;
public class CalculatorImpl implements Calculator {
@Override
public int add(int i, int j) {
int result = i + j;
System.out.println("方法内部 , result: "+result);
return result;
}
@Override
public int sub(int i, int j) {
int result = i - j;
System.out.println("方法内部 , result: "+result);
return result;
}
@Override
public int mul(int i, int j) {
int result = i * j;
System.out.println("方法内部 , result: "+result);
return result;
}
@Override
public int div(int i, int j) {
int result = i / j;
System.out.println("方法内部 , result: "+result);
return result;
}
}
代理工程类:
package com.wfy.proxy;
public class CalculatorStaticProxy implements Calculator{
private CalculatorImpl target;
public CalculatorStaticProxy(CalculatorImpl target) {
this.target = target;
}
@Override
public int add(int i, int j) {
System.out.println("日志 , 方法: add , 参数: "+i+","+j);
int result=target.add(i,j);
System.out.println("日志 , 方法: add , 结果: "+result);
return result;
}
@Override
public int sub(int i, int j) {
System.out.println("日志 , 方法: sub , 参数: "+i+","+j);
int result=target.sub(i,j);
System.out.println("日志 , 方法: sub , 结果: "+result);
return result;
}
@Override
public int mul(int i, int j) {
System.out.println("日志 , 方法: mul , 参数: "+i+","+j);
int result=target.mul(i,j);
System.out.println("日志 , 方法: mul , 结果: "+result);
return result;
}
@Override
public int div(int i, int j) {
System.out.println("日志 , 方法: div , 参数: "+i+","+j);
int result=target.div(i,j);
System.out.println("日志 , 方法: div , 结果: "+result);
return result;
}
}
测试:
package com.wfy.Proxy;
import com.wfy.proxy.CalculatorImpl;
import com.wfy.proxy.CalculatorStaticProxy;
import org.junit.Test;
public class ProxyTest {
@Test
public void ProxyTest(){
CalculatorStaticProxy calculator = new CalculatorStaticProxy(new CalculatorImpl());
calculator.add(1,2);
}
}
结果:
静态代理的缺点:
静态代理确实实现了解耦,但是由于代码都写死了,完全不具备任何灵活性。就拿日志功能来说,将来其他地方也需要附加日志,还需要声明更多的静态代理类,会产生大量的重复代码,日志功能还是分散,没有统一管理。
动态代理
jdk动态代理
jdk动态代理 ,要求必须有接口, 最终生成的代理类和目标类实现相同的接口,在com.sun.proxy包下, 类名为$proxy2
创建一个ProxyFactory实现类
package com.wfy.proxy;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class ProxyFactory {
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
public Object getProxy(){
/**
* ClassLoader loader: 指定加载动态生成的代理类的类加载器
* Class<?>[] interfaces: 获取目标对象实现的所有接口的class对象的数组
* InvocationHandler h : 设置代理类中的抽象方法如何重写
*
*/
Class<?>[] interfaces = target.getClass().getInterfaces();
ClassLoader classLoader = this.getClass().getClassLoader();
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("日志 , 方法: "+method.getName()+", 参数: "+ Arrays.toString(args));
//proxy表示代理对象,method表示要执行的方法,args表示要执行的方法到的参数列表
Object result = method.invoke(target, args);
System.out.println("日志 , 方法: "+method.getName()+", 结果: "+ result);
return result;
}
};
return Proxy.newProxyInstance(classLoader,interfaces,h);
}
}
测试:
package com.wfy.Proxy;
import com.wfy.proxy.Calculator;
import com.wfy.proxy.CalculatorImpl;
import com.wfy.proxy.CalculatorStaticProxy;
import com.wfy.proxy.ProxyFactory;
import org.junit.Test;
public class ProxyTest {
@Test
public void ProxyTest(){
ProxyFactory proxyFactory = new ProxyFactory(new CalculatorImpl());
Calculator proxy = (Calculator) proxyFactory.getProxy();
proxy.add(1, 2);
}
}
cglib动态代理
cglib动态代理,最终生成的代理类会继承目标类,并且和目标类在相同的包下
AOP概念及相关术语
概念:AOP(Aspect Oriented Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面向对象编程的一种补充和完善,它以通过预编译和运行期动态代理方式实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术。
相关术语
横切关注点
从每个方法中抽取出来的同一类非核心业务。在同一个项目中,我们可以使用多个横切关注点对相关方法进行多个方面的增强。
这个概念不是语法层面天然存在的,而是根据附加功能的逻辑上的需要:有十个附加功能,就有十个横切关注点。
通知
每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法就叫通知方法。
- 前置通知:在被代理的目标方法前执行
- 返回通知:在被代理的目标方法成功结束后执行(寿终正寝)
- 异常通知:在被代理的目标方法异常结束后执行(死于非命)
- 后置通知:在被代理的目标方法最终结束后执行 (盖棺定论)
- 环绕通知:使用try...catch...finally结构围绕整个被代理的目标方法,包括上面四个通知对应的所有位置
切面
封装通知方法的类
目标
被代理的目标对象
代理
向目标对象应用通知之后创建的代理对象
连接点
这也是一个纯逻辑概念,不是语法定义的
把方法排成一排,每一个横切位置看成x轴方向,把方法从上到下执行的顺序看成y轴,x轴和y轴的交叉点就是连接点
切入点
定位连接点的方式
每个类的方法中都包含多个连接点,所以连接点是类中客观存在的事物(从逻辑上来说)。
如果把连接点看作数据库中的记录,那么切入点就是查询记录的sql语句。
Spring的AOP技术可以通过切入点定位到特定的连接点。
切点通过org.springframework.op.Poincut接口进行描述,它使用类和方法作为连接点的查询条件。
作用
- 简化代码:把方法中固定位置的重复的代码抽取出来,让被抽取的方法更专注于自己的核心功能,提高内聚性。
- 代码增强:把特定的功能封装到切面类中,看哪里有需要,就往上套,被套用了切面逻辑的方法就被切面给增强了。