zoukankan      html  css  js  c++  java
  • Spring入门(二)— IOC注解、Spring测试、AOP入门

    一、Spring整合Servlet背后的细节

    1. 为什么要在web.xml中配置listener

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    配置listener主要是为了捕获项目发布 | 服务器启动的契机 ,为了解析xml , 创建工厂。 这个listener是spring官方提供的,里面已经具备了解析xml 和 创建工厂的代码。

    2. 为什么要在web.xml中配置context-param

      <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
      </context-param>

    正如上面所说的,listener捕获到了项目发布的契机,进而去创建工厂。但是创建工厂需要解析xml 。 spring的这个监听器里面,默认会到WEB-INF/applicationContext.xml. 如果不想放置到这个位置,可以通过一个context-param来告诉spring,我们的配置文件在哪里。 classpath 表示这个文件是位于类路径底下。 classes目录

    3. 为什么使用工具类也能拿到工厂,到底工厂放在了哪里?  

    public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
        
        //项目一发布,就执行
        public void contextInitialized(ServletContextEvent event) {
            //创建工厂
            initWebApplicationContext(event.getServletContext());
        }
    }
    ​
    public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
            ...
            if (this.context == null) {
                 //创建工厂
                 this.context = createWebApplicationContext(servletContext);
            }
                                 
     //把创建好的工厂,存储到作用域     
    //servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
    this.context); ... } ​ //spring为了简化程序员获取工厂的工作, 就提供了一个工具类,其实这个工具类就是对取值的代码进行封装 ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); //ApplicationContext context = (ApplicationContext) getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);

    二、IOC注解

    1. 注解入门

    1. 导入jar包

      spring-aop-xxx.jar

    2. 导入约束

    <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" 
        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"
    >
    1. 在xml里面打开注解扫描开关

    <!-- 告诉spring要去解析类,因为类上有注解 -->
    <context:component-scan base-package="com.pri.service.impl"/>
    1. 在托管的类上打注解

    @Component("us") 
    public class UserServiceImpl implements UserService {
        ...
    }
    1. 问工厂要实例对象

    //创建工厂 创建工厂,需要解析xml ,所以要传递进去xml文件
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            
    //问工厂要实例对象
    UserService userService = (UserService) context.getBean("us");
            
    //调用方法
    userService.save();
            
    //关闭工厂 : 以后我们几乎不会关闭工厂。
    ((AbstractApplicationContext) context).close();

    2. 注解创建对象

    /*
     1. @Component组件,表示要托管这个类,让spring创建这个类的实例。 括号里面的us 其实就是id标识符
     2. @Component 是通用的注解, 对任何托管的类都可以使用,但是spring为了迎合三层架构,所以对每一层
     也给出了具体的注解。
            Action  --- @Controller
            Service --- @Service
            Dao --- @Repository :仓库,
        建议: 如果以后托管三层中的类,请使用具体对应的注解,如果托管的是普通的其他类。@Component
     3. 默认生成的实例还是单例,如果想做成多例,那么还得添加一个注解
            @Scope("prototype")
     4.  @PostConstruct  //初始化实例的时候调用
         @PreDestroy  //销毁实例之前,调用
     5. 如果使用注解托管某一个类,不写属性值,那么默认的id标识符就是类的名字(首字母是小写) userServiceImpl
     6.<!-- 如果想要扫描多个包,就写一个通用的前缀即可 -->
    <context:component-scan base-package="com.pri"/>
    */

    3. DI注解开发(注入对象)

    使用注解来完成依赖注入。 一般注解注入,它针对的点是对象的注入。 spring针对对象的注入,提供了两个注解 @Resource@Autowired

    • 常用的注解就两个 @Resource & @Autowired

    @Resource(name="ud") 根据给定的标记找到对应的类,创建对象,注入进来。

    @Autowired 自动装配,会找到对应的实现类创建对象,注入进来。但是如果存在多个实现,那么会抛出异常

    @Repository("ud")
    public class UserDaoImpl implements UserDao {
    }
    
    public class UserServiceImpl implements UserService {   
        @Resource(name="ud") //spring拿着ud找到具体的类,然后创建实例,注入进来。
        private UserDao userDao;
        ...
    }
    ----------------------------------------------------------
    public class UserServiceImpl implements UserService {
         @Autowired  //自动装配 根据注入的接口类型找到对应的实现类,注入进来。
         private UserDao userDao;
            ...
    }

    4. xml和注解混合使用

    在项目里面,使用xml和注解来完成Spring 的配置。

    xml : 负责完成IOC (对象的创建)

    注解 : 负责完成DI (属性的注入)

    xml托管类
    <context:component-scan base-package="com.pri"/>
    <bean id="ud" class="com.pri.dao.impl.UserDaoImpl"></bean>
    <bean id="us" class="com.pri.service.impl.UserServiceImpl"></bean>
    注解完成注入:
     public class UserServiceImpl implements UserService {
         @Resource(name="ud")
         private UserDao userDao;
     }

    三、Spring测试

    1. 导入jar包

      spring-test-xxx.jar

    2. 托管业务逻辑类,不管是用xml还是注解都可以

    3. <bean id="us" class="com.pri.service.impl.UserServiceImpl"></bean>
    1. 在测试类上打上注解 ,给测试类的成员变量注入值

    //spring扩展了junit的运行环境,除了有测试功能之外,还在里面定义了创建工厂的代码
    @RunWith(SpringJUnit4ClassRunner.class)
    ​
    //告诉spring的测试环境,配置文件在哪里
    @ContextConfiguration("classpath:applicationContext.xml")
    public class TestUserService {
        
        //测试类里面出现的注解,不用打开扫描开关。因为这个测试环境里面,它会解析这个测试类的注解。 
        @Autowired
        private UserService userService;
        @Test
        public void testSave(){
            userService.save();
        }
    }

    四、AOP

    1. 什么是AOP , 它有什么用?

    AOP(Aspect Oriented Programming,面向切面编程),可以说是OOP(Object Oriented Programing,面向对象编程)的补充和完善。OOP更多的是侧重于上下间的关系(继承关系 、实现关系) , OOP很难体现左右间的关系。 核心: 在不改动源码的前提下,对原有功能能完成扩展 | 升级

      

    2. AOP的底层原理

    aop不改源码,但是能够扩展和升级代码。 能够做成这个事情,只有三种解决手法 : 装饰者模式静态代理动态代理 。 AOP 选择的是动态代理 , 装饰者模式和静态代理,要求我们必须写出来装饰类和代理类。 动态代理的实现机制,有两种: 基于JDK的动态搭理基于Cglib的动态代理

    1. 动态代理的实现机制

    • 基于JDK的动态代理

    如果哪一个真实类有实现接口,那么就采用这种方式,创建出来接口的另一个实现类作为代理类

    //jdk动态代理
    @Test
    public void testJDKPorxy(){
        
        //UserService userService = new UserServiceImpl();
        //userService.save();
        
        //1. 先创建真实对象
        final UserService userService = new UserServiceImpl();
        
        //2. 创建代理对象
        UserService proxyObj = (UserService) Proxy.newProxyInstance(
                userService.getClass().getClassLoader(),  //类加载器,真实类用什么,代理类就用什么
                userService.getClass().getInterfaces(),  //真实类实现什么接口,代理类也实现什么接口
                new InvocationHandler() {//回调函数
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("invoke~");
                        //userService.save();
                        
                        if(method.getName().equals("save")){
                            Logger.log();
                        }
                        
                        //以不变应万变。 反射调用
                        return method.invoke(userService, args);
                    }
                }); 
        
        //3. 让代理对象干活
        proxyObj.save();  //代理对象。save()  ----> 真实对象.save();
    }
    • 基于Cglib动态代理

    如果真实类是一个普通类,没有实现接口,那么就采用这种方式, 创建出来真实类的子类作为代理类。

    //cglib动态代理
    @Test
    public void testCglibPorxy(){
        //1. 一定要有真实对象
        final ProductService productService = new ProductService();
        //2. 创建代理
        Enhancer  enhancer = new Enhancer();
        //设置父类是谁
        enhancer.setSuperclass(ProductService.class);
        //设置回调
        enhancer.setCallback(new MethodInterceptor() {
            /*
             * arg0 :代理对象
             * arg3 : 方法的代理 
             * 
             * 一般这两不用。
             * 
             * arg1 : 方法引用
             * arg2 :参数
             */
            @Override
            public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
                Logger.log();
                return arg1.invoke(productService, arg2);
            }
        });
        
        //创建代理对象
        ProductService proxyObj = (ProductService) enhancer.create();
        proxyObj.save(); 
    }

    3. AOP术语

    4.AOP的入门

    Spring的AOP其实已经准备好了创建代理的代码。只是不知道的是要创建谁的代码。哪些方法需要被增强。我们需要通过配置的形式告诉spring。

    1. 定义业务逻辑类

    public class UserServiceImpl implements UserService {
    ​
        @Override
        public void save() {
            System.out.println("调用了UserServiceImpl 的 save方法");
        }
    }
    1. 定义增强类

    public class Logger {
        public static void log(){
            System.out.println("输出日志了~~");
        }
    }
    1. 导入jar包

        a. 导入 spring必须的jar

    b. 额外导入:

    spring-aop-xx.jar,spring-aspect-xx.jar

    面向切面过程中,Spring AOP是遵循了AOP联盟的规范实现的,所以需要有AOP联盟的接口包
    aopalliance-x.x.jar,接口包依赖aspectjweaver-x.x.x.jar
    1. xml中配置

    要导入aop的约束

    让spring托管 业务逻辑类 和 增强类
    <bean id="us" class="com.pri.service.impl.UserServiceImpl" ></bean>
    <bean id="logger" class="com.pri.util.Logger" ></bean>
    配置AOP
    <!-- 2. 开始配置aop -->
        <aop:config>
            <!-- 配置切入点  expression 表达式 '
                execution(* com.xyz.myapp.service.*.*(..))
                execution 固定写法
                第一个* 代表任意返回值
                com.xyz.myapp.service : 包名
                第二个* 包下的任意类
                第三个* 类中的任意方法
                (..) : 任意参数
            saveUser
            saveOrder
            -->
            <aop:pointcut expression="execution(* com.pri.service.impl.*.*(..))" id="aa"/>
            <!-- 配置增强 
                根据aa的表达式找到的方法,都给他们做前置增强,增强的功能是log方法
            -->
            <aop:aspect ref="logger"> 
                <aop:before method="log" pointcut-ref="aa"/>
            </aop:aspect>
        </aop:config>

    5. AOP 增强

    <aop:config>
      <aop:pointcut expression="execution(* com.pri.service.impl.*.*(..))" id="pointcut01"/> <!-- 真正用aop来扩展一个功能,比较少。 除非是我们想扩展第三方jar包。 aop的思想无处不在:struts 拦截器 (就是AOP) --> <!-- 配置切面aspect --> <aop:aspect ref="logger"> <!-- 前置增强 --> <!-- <aop:before method="log" pointcut-ref="pointcut01"/> --> <!-- 最终增强 --> <!-- <aop:after method="log" pointcut-ref="pointcut01"/> --> <!-- 后置增强 --> <!-- <aop:after-returning method="log" pointcut-ref="pointcut01"/> --> <!-- 异常增强 --> <!-- <aop:after-throwing method="log" pointcut-ref="pointcut01"/> --> <!-- 环绕增强 --> <!-- <aop:around method="around" pointcut-ref="pointcut01"/> --> <aop:before method="log" pointcut-ref="pointcut01"/> <aop:after-returning method="log" pointcut-ref="pointcut01"/> </aop:aspect> </aop:config>
     
  • 相关阅读:
    iOS蓝牙开发(4.0)详解
    iOS开发--MQTT实时处理数据
    BBWebImage 设计思路
    SDWebImage 加载显示 GIF 与性能问题
    iOS 图片压缩方法
    SDWebImage 加载显示 WebP 与性能问题
    NSTimer、CADisplayLink 内存泄漏
    iOS 使用矢量图
    LeetCode #36 Valid Sudoku
    LeetCode #35 Search Insert Position
  • 原文地址:https://www.cnblogs.com/gdwkong/p/8453162.html
Copyright © 2011-2022 走看看