1. 代理模式
为什么要学习代理模式,因为AOP的底层机制就是动态代理!
代理模式:
- 静态代理
- 动态代理
1.1 静态代理
静态代理角色分析
- 抽象角色 : 一般使用接口或者抽象类来实现
- 真实角色 : 被代理的角色
- 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作
- 客户 : 使用代理角色来进行一些操作
代码实现
Rent . java 即抽象角色
//抽象角色:租房
public interface Rent {
public void rent();
}
Host . java 即真实角色
//真实角色: 房东,房东要出租房子
public class Host implements Rent{
public void rent() {
System.out.println("房屋出租");
}
}
Proxy . java 即代理角色
//代理角色:中介
public class Proxy implements Rent {
private Host host;
public Proxy() { }
public Proxy(Host host) {
this.host = host;
}
//租房
@Override
public void rent(){
seeHouse(); // 扩展功能
host.rent();
fare(); // 扩展功能
}
//看房
public void seeHouse(){
System.out.println("带房客看房");
}
//收中介费
public void fare(){
System.out.println("收中介费");
}
}
测试代码
//客户类,一般客户都会去找代理!
public class Client {
public static void main(String[] args) {
//房东要租房
Host host = new Host();
//中介帮助房东
Proxy proxy = new Proxy(host);
//你去找中介!
proxy.rent();
}
}
分析: 在这个过程中,你直接接触的就是中介,就如同现实生活中的样子,你看不到房东,但是你依旧租到了房子,房东的房子通过代理,这就是所谓的代理模式
1.2 静态代理的好处
- 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
- 公共的业务由代理来完成 . 实现了业务的分工 ,
- 公共业务发生扩展时变得更加集中和方便 .
缺点 :
- 类多了 , 多了代理类 , 工作量变大了 . 开发效率降低
我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想
我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 !
2.1 动态代理
- 动态代理的角色和静态代理的一样
- 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
- 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
- 基于接口的动态代理----JDK动态代理
- 基于子类的动态代理–cglib
- 现在用的比较多的是 javasist 来生成动态代理 . 百度一下javasist
- 动态代理也有个约束:目标对象一定是要有接口的,没有接口就不能实现动态代理
2.1 动态代理通用实现类
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;
public void setTarget(Object target) {
this.target = target;
}
// 生成代理类
// 第三个参数this指代调用了public Object invoke(Object proxy, Method method, Object[] args)方法
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
// proxy : 代理类
// method : 代理类的调用处理程序的方法对象.
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(target, args);
after();
return result;
}
private void after() {
System.out.println("执行真实对象方法之后");
}
// 扩展方法
public void before() {
System.out.println("执行真实对象方法之前");
}
}
测试代码
@Test
public void test16() {
ProxyInvocationHandler proxy = new ProxyInvocationHandler();
// 参数中注入真实对象(被代理的对象)
proxy.setTarget(new UserServiceImpl());
UserService userProxy = (UserService) proxy.getProxy();
userProxy.add();
}
2.2 动态代理的好处
静态代理有的它都有,静态代理没有的,它也有!
- 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
- 公共的业务由代理来完成 . 实现了业务的分工 ,
- 公共业务发生扩展时变得更加集中和方便 .
- 一个动态代理 , 一般代理某一类业务
- 一个动态代理可以代理多个类,代理的是接口!
2. AOP
2.1 什么是AOP
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术(代理模式)。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
2.2 Aop在Spring中的作用
提供声明式事务;允许用户自定义切面
- 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …
- 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
- 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
- 目标(Target):被通知对象。
- 代理(Proxy):向目标对象应用通知之后创建的对象。
- 切入点(PointCut):切面通知 执行的 “地点”的定义。
- 连接点(JointPoint):与切入点匹配的执行点。
2.3 使用Spring实现AoP
【重点】使用AOP织入,需要导入一个依赖包!
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
2.4 第一种方式(自定义实现)
编写业务接口
public interface UserService {
public void add();
public void delete();
public void update();
public void search();
}
编写业务实现类
public class UserServiceImpl implements UserService{ @Override public void add() { System.out.println("增加用户"); } @Override public void delete() { System.out.println("删除用户"); } @Override public void update() { System.out.println("更新用户"); } @Override public void search() { System.out.println("查询用户"); }}
写一个切入类
public class DiyPointcut { public void before(){ System.out.println("---------方法执行前---------"); } public void after(){ System.out.println("---------方法执行后---------"); }}
去spring中配置,并实现aop切入实现 , 注意导入约束 .
<?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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--自定义实现--> <!--注册bean--> <bean id="diy" class="com.it.config.DiyPointcut"/> <bean id="userservice2" class="com.it.service.UserServiceImpl"/> <!--aop的配置--> <aop:config> <!--第二种方式:使用AOP的标签实现--> <!--定义切面切面--> <aop:aspect ref="diy"> <!--设置切入点 "execution(* com.it.service.UserServiceImpl.*(..))" 第一个参数为返回值,第二个参数为包名+类名+方法名+(参数名)--> <aop:pointcut id="diyPonitcut" expression="execution(* com.it.service.UserServiceImpl.*(..))"/> <!--method:执行切面中的哪个方法 pointcut-ref:切入到哪个地方--> <aop:before pointcut-ref="diyPonitcut" method="before"/> <aop:after pointcut-ref="diyPonitcut" method="after"/> </aop:aspect> </aop:config></beans>
测试代码
@Testpublic void test17() { ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml"); UserService userservice = (UserService) context.getBean("userservice2"); System.out.println(userservice); userservice.add(); userservice.update(); userservice.delete(); userservice.query();}
2.5 第二种方式(注解)
编写一个注解实现的增强类(这个类必须有spring进行托管)
@Component@Aspectpublic class AnnotationPointcut { @Before("execution(* com.kuang.service.UserServiceImpl.*(..))") public void before(){ System.out.println("---------方法执行前---------"); } @After("execution(* com.kuang.service.UserServiceImpl.*(..))") public void after(){ System.out.println("---------方法执行后---------"); } <!--环绕执行:ProceedingJoinPoint中包含了对真实对象的一层扩展--> @Around("execution(* com.kuang.service.UserServiceImpl.*(..))") public void around(ProceedingJoinPoint jp) throws Throwable { System.out.println("环绕前"); System.out.println("签名:"+jp.getSignature()); //执行目标方法proceed Object proceed = jp.proceed(); System.out.println("环绕后"); System.out.println(proceed); }}
在Spring配置文件中,注册bean,并增加支持AOP注解的配置
@Configuration // 声明该类是一个spring配置类@EnableAspectJAutoProxy // 开启spring aop注解@ComponentScan("com.it.log") // 扫描该包下的组件public class MyConfig { @Bean public UserDaoImpl userDao() { return new UserDaoImpl(); } @Bean public UserServiceImpl userService() { return new UserServiceImpl(); }}
aop:aspectj-autoproxy:说明
<aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,当配为<aop:aspectj-autoproxy poxy-target-class="true"/>时,表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。