这是我参与11月更文挑战的第11天,活动详情查看:2021最后一次更文挑战
1.现有业务层开发存在问题
a.定义业务接口
public interface UserService {
void save(String name);
void delete(String id);
void update();
}
复制代码
b.实现业务接口
public class UserServiceImpl implements UserService {
@Override
public void save(String name) {
try {
System.out.println("开启事务");
System.out.println("处理业务逻辑,调用DAO~~~");
System.out.println("提交事务");
}catch (Exception e){
System.out.println("回滚事务");
e.printStackTrace();
}
}
@Override
public void delete(String id) {
try {
System.out.println("开启事务");
System.out.println("处理业务逻辑,调用DAO~~~");
System.out.println("提交事务");
}catch (Exception e){
System.out.println("回滚事务");
e.printStackTrace();
}
}
@Override
public void update() {
try {
System.out.println("开启事务");
System.out.println("处理业务逻辑,调用DAO~~~");
System.out.println("提交事务");
}catch (Exception e){
System.out.println("回滚事务");
e.printStackTrace();
}
}
}
复制代码
问题:从上图中可以看出,现有业务层中控制事务代码出现了大量的冗余
,如何解决现有业务层出现的冗余问题?
2.代理引言
a.什么是代理
代理
: 指的是java中的一种设计模式
b.为什么需要代理
很多时候除了当前类能够提供的功能外,我们还需要补充一些额外功能。
c.代理的作用
代理对象可以在客户和目标对象之间
起到中介作用,从而为目标对象增添额外的功能
。代理对象保证原始功能不变,从而进一步增加额外功能
将控制事务代码交给代理执行,业务层只专注于业务逻辑对象
d.代理图例
3.静态代理的开发
目标类|对象(target)
:被代理类称之为目标类|或者被代理的对象称之为目标对象
开发代理的原则: 代理类和目标类功能一致且实现相同的接口,同时代理类中依赖于目标类对象
a.开发静态代理类
//静态代理类
//开发原则:代理类和目标类实现相同接口,依赖于真正的目标类
public class UserServiceStaticProxy implements UserService {
//真正的目标类
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
@Override
public void save(String name) {
try {
System.out.println("开启事务");
userService.save(name);//调用真正业务逻辑方法
System.out.println("提交事务");
}catch (Exception e){
System.out.println("回滚事务");
e.printStackTrace();
}
}
@Override
public void delete(String id) {
try {
System.out.println("开启事务");
userService.delete(id);//调用真正业务逻辑方法
System.out.println("提交事务");
}catch (Exception e){
System.out.println("回滚事务");
e.printStackTrace();
}
}
@Override
public void update() {
try {
System.out.println("开启事务");
userService.update();//调用真正业务逻辑方法
System.out.println("提交事务");
}catch (Exception e){
System.out.println("回滚事务");
e.printStackTrace();
}
}
}
复制代码
b.更改目标实现类
public class UserServiceImpl implements UserService {
@Override
public void save(String name) {
System.out.println("处理业务逻辑,调用DAO~~~");
}
@Override
public void delete(String id) {
System.out.println("处理业务逻辑,调用DAO~~~");
}
@Override
public void update() {
System.out.println("处理业务逻辑,调用DAO~~~");
}
}
复制代码
c.配置静态代理类
<!--配置目标类-->
<bean id="userService" class="staticproxy.UserServiceImpl"></bean>
<!--配置代理类-->
<bean id="userServiceStaticProxy" class="staticproxy.UserServiceStaticProxy">
<!--注入目标对象-->
<property name="userService" ref="userService"/>
</bean>
复制代码
d.调用代理方法
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userServiceStaticProxy = (UserService) context.getBean("userServiceStaticProxy");
userServiceStaticProxy.save("小黑");
复制代码
新的问题:往往在开发我们书写的不仅仅是一个业务层,两个业务层,而我们的业务层会有很多,如果为每一个业务层开发一个静态代理类,不仅没有减轻工作量,甚至让我们的工作量多了一倍不止怎么解决以上这个问题呢?
解决方案: 为业务层在运行过程中动态创建代理类,通过动态代理类去解决我们现有业务层中业务代码冗余的问题
.
4.动态代理的原理
通过jdk提供的Proxy这个类,动态为现有的业务生成代理类
参数一:当前线程类加载器 参数二:生成代理类的接口类型 参数三:通过代理类对象调用方法时会优先进入参数三中的invoke方法Proxy.newProxyInstance(loader, interfaces, h);//返回值就是动态代理对象
public class TestDynamicProxy {
public static void main(String[] args) {
final UserService userService = new UserServiceImpl();
//参数1:当前线程类加载器
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
//参数2:
Class[] classes = new Class[]{UserService.class};
//参数3:
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(contextClassLoader, classes, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try{
System.out.println("开启事务");//附加操作
Object invoke = method.invoke(userService, args);
System.out.println("提交事务");//附加操作
return invoke;
}catch (Exception e){
System.out.println("回滚事务");//附加操作
}
return null;
}
});
userServiceProxy.save("小黑");
}
}
复制代码
5.AOP编程
AOP定义:通过为项目中某些组件在程序运行过程中动态生成代理对象,通过代理对象解决现有项目中通用问题如:事务控制、日志记录、性能统计等。
通知(Advice)
: 除了目标方法以外的操作都称之为通知
切入点(PointCut): 要为哪些类中的哪些方法加入通知
切面(Aspect)
: 通知 + 切入点
5.2 编程步骤
# 1.引入依赖
spring-aop
spring-expression
spring-aspects
# 2.开发通知类
MethodBeforeAdvice 前置通知
MethodInterceptor 环绕通知
AfterReturningAdvice 返回后通知
ThrowsAdvice 异常通知
MyAdvice implements 通知接口{.....}
//自定义通知类:用来完成额外功能
public class MyAdvice implements MethodBeforeAdvice {
@Override//参数1:当前调用的方法对象 //参数2:当前调用方法对象的参数 //参数3:目标对象
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("目标方法名: "+method.getName());
System.out.println("目标方法的参数: "+objects);
System.out.println("目标对象: "+o.getClass());
}
}
# 3.配置切面
a.引入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">
b.管理通知
<!--管理通知类-->
<bean id="myAdvice" class="before.MyAdvice"/>
c.配置切面
<aop:config>
<aop:pointcut id="pc" expression="execution(* before.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="myAdvice" pointcut-ref="pc"/>
</aop:config>
# 4.启动工厂测试
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("before/spring.xml");
UserService userSerivce = (UserService) context.getBean("userService");
System.out.println(userSerivce.getClass());
userSerivce.save("小黑");
复制代码
5.3 前置通知的使用
5.4 环绕通知的使用
5.5 返回后通知
5.6 异常通知
6.切入点表达式
注意:方法级别的切入点表达式尽可能精准,否则程序运行可能出现异常
# 1.语法
within(包.类)
# 2.示例
within(com.xxx.service.*)
包: com.xxx.service
类: 所有类中所有方法不关心返回值和参数
within(com.xxx.service.UserServiceImpl)
包: com.xxx.service
类: UserServiceImpl类中所有方法不关心返回值和参数
复制代码
注意:within的效率高于execution表达式,推荐使用within表达式
注意:被切入点切中组件获取的是代理对象不再是原始对象
7.多个切面的执行顺序
-
多个aop切面的执行顺序默认是配置顺序
-
在标签<aop:advisor advice-ref pointcut-ref order=" " />
order int 整数 0 1 2 3 4 ... 数字越小优先执行,一旦指定order属性则配置顺序失效,order值相同,配置顺序优先
8.spring框架中创建代理对象的方式及区别
① JDK : 提供Proxy 根据接口生成代理对象 默认使用jdk中的Proxy
② Spring : 提供CGLIB 根据实现类生成代理对象
修改spring框架默认生成代理对象为CGLIB:
<aop:config proxy-target-class="false(Proxy)|true(CGLIB)">
近期评论