Spring框架(二)

接Spring框架(一)

 

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.1  通知分类

 

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.切入点表达式

6.1  execution方法级别的切入点表达式

注意:方法级别的切入点表达式尽可能精准,否则程序运行可能出现异常

6.2  within类级别的切入点表达式

# 1.语法
  within(包.类)

# 2.示例
  within(com.xxx.service.*) 
      包: com.xxx.service
      类: 所有类中所有方法不关心返回值和参数

  within(com.xxx.service.UserServiceImpl)
      包: com.xxx.service
      类: UserServiceImpl类中所有方法不关心返回值和参数

注意:within的效率高于execution表达式,推荐使用within表达式

注意:被切入点切中组件获取的是代理对象不再是原始对象

7.多个切面的执行顺序

1. 多个aop切面的执行顺序默认是配置顺序

2. 在标签<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)">

 

 

        点击:Spring框架(三)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值