zoukankan      html  css  js  c++  java
  • Spring第二天——IOC注解操作与AOP概念

      大致内容

        spring的bean管理(注解实现)
        AOP原理
        log4j介绍
        spring整合web项目的演示

    一、spring注解实现bean管理

      注解:
        代码中一些特殊的标记,使用注解也可以完成一些相关的功能(写法"@")
        方法上、类上(详见基础加强) 

      使用注解创建对象,注入属性(完成day01相似的功能)
      可以使用注解,但不可能完全替代xml配置文件 【更新】 还真难说,有可能全注解无配置开发 

      准备工作:
        导入包:除了day01的6个核心jar包(当然包括日志的包)
            再加上aop的那个jar包(注解功能在里面),也就是图中红线的jar包

              

        创建User类和测试方法add():

    public class User {
    
        public void add(){
            System.out.println("注解的:add...");
        }
    } 

      创建xml文件后需要引入约束:
        除了第一天的beans约束;还需要一个用于注解的约束
        (也是找html文件夹最后一个文件),引入context的约束

       也就是打开xsd-configuration.html后找到这段复制进来 (已由springIDE替代)

         修改后的配置文件如下(还可以使用filter过滤器来进行过滤):

    <?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> 
    
        <!-- 开启注解扫描 ,scan就是扫描之意-->
        <context:component-scan base-package="cn.anotation"></context:component-scan>
        <!-- 只扫描属性上的注解,用的少 -->
        <!-- <context:annotation-config></context:annotation-config> -->
    </beans>

      准备工作准备完毕后,可以开始注解开发:
        1.注解创建对象:
         在要创建的对象的类上加注解:
         用 @Component(value="user")注解

        其它的配置文件中提到的这里也可以用注解实现,例如:bean的作用域 Scope="" 可以在类上加@Scope()注解
      虽然目前这3 个注释和 @Component 相比没有什么新意,但 Spring 将在以后的版本中为它们添加特殊的功能。
      所以,如果 Web 应用程序采用了经典的三层分层结构的话,最好在持久层、业务层和控制层分别采用上述注解对分层中的类进行注释。

      @Service 用于标注业务层组件 ===业务层

      @Controller 用于标注控制层组件(如struts中的action)===WEB层

      @Repository 用于标注数据访问组件,即DAO组件 ===持久层

      @Component 泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。

      可以加个注解,控制是单实例或者多实例
      在类上多加一行注解(内容参考昨天的,singleton但,prototype)
      @Scope(value="prototype") //设置是单实例还是多实例
     【更新!】spring的组件扫描可以侦测和实例化带有特定注解的类:

        对于扫描到的组件,spring有默认的命名策略(第一个字母小写),当然我们可以在注解后显式的声明名称 @Controller("userController")

        并且可以设置只扫描特定的类:

      可以为扫描的注解配置子节点:(可以有若干个)

    <context:include-filter> 子节点表示要包含的目标类

    <context:exclude-filter> 子节点表示要排除在外的目标类

      示例:

        <!-- @Service扫描 -->
        <context:component-scan base-package="cn.service">
            <context:exclude-filter type="annotation" expression="cn.service.ItemsService"/>
        </context:component-scan>

      可以设置不使用默认的过滤器来达到效果:

    <context:component-scan base-package="cn.service" use-default-filters="false">

     【更新】:设置只扫描controller注解

    <!-- springMVC的配置文件,跳转逻辑有关的 -->
        <context:component-scan base-package="cn.crud" use-default-filters="false">
            <!-- 配置只扫描@Controller注解 -->
            <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        </context:component-scan>

     这样就会只扫描base-package指定下的有@Controller下的java类,并注册成bean

      实际上<context:component-scan> 元素还会自动注册 AutowiredAnnotationBeanPostProcessor 实例,

        该实例可以自动装配具有 @Autowired@Resource 、@Inject注解的属性.

      其中:@Autowired  会自动装配类型兼容的bean,构造器, 普通字段(即使是非 public), 一切具有参数的方法(set()方法等)都可以应用@Authwired 注解,注意set()方法时判断字段名称的方法

       当 Spring 找不到匹配的 Bean 装配属性时, 会抛出异常, 若某一属性允许不被设置, 可以设置 @Authwired 注解的 required 属性为 false

      @Autowired

      当有多个匹配的实例时,会去匹配名称相匹配的,若再相同,则报错!

    @Autowired 注解也可以应用在数组类型的属性上, 此时 Spring 将会把所有匹配的 Bean 进行自动装配.

    @Autowired 注解也可以应用在集合属性上, 此时 Spring 读取该集合的类型信息, 然后自动装配所有与之兼容的 Bean.

    @Autowired 注解用在 java.util.Map 上时, 若该 Map 的键值为 String, 那么 Spring 将自动装配与之 Map 值类型兼容的 Bean, 此时 Bean 的名称作为键值

    【更新】 spring4的新特性:泛型依赖注入

      注解配置后的User类如下:

    package cn.anotation;
    
    import org.springframework.stereotype.Component;
    
    
    /*@Scope(value="prototype") //设置是单实例还是多实例*/
    @Component(value="user") //相当于原来的<bean id=use>r的配置
    public class User {
    
        public void add(){
            System.out.println("注解的:add...");
        }
    }

      2.注解注入属性:

      (之前用的是默认的byName形式),其中byName需要与set()方法对应,而不是和属性名称对应
      还是使用service中注入dao对象(对比day01的xml的配置实现)
      对比xml实现步骤:还是需要先创建对象,再实现注入
      先在两个类上加类注解
      再在service中加dao成员变量dao,给成员变量加注解而无需set()方法:

      类型有 byName byType:

        配置文件中不能有俩个相同的id,否则容器启动失败
      主要有两个属性装配的注解:
        @Autowried :自动注入找到对象的方式是根据类名找的对象进行注入——适用于单实现,底层原理是byType,可以利用@Qulifiler 来指定名称

    1. @Autowired
    2.  @Qualifier("baseDao")     
    3. private BaseDao baseDao; 
    4. 在配置文件中使用:

        <qualifier value=""></qualifier>

        甚至 ,,可以将此注解应用在list map(key 必须是String)
        (与dao的类注解的value值无关),比较不直观,没有指定对象
        【注解更清晰】@Resource(name="要注入的对象名(dao的类注解的value值)")——适用于多实现

                这是由JavaEE5提供的

          一般而言,dao与daoImpl使用注解时需要在实现类中指定名称为接口
        若与value值不一样会报错(找错比较快的方式是找Casue By这一行,错误最准确)

      JSR330 的Inject 待补充

     注解配置完成后的service和dao类如下:

    package cn.anotation;
    
    import org.springframework.stereotype.Component;
    
    @Component(value="userDao")
    public class UserDao {
    
        public void add(){
            System.out.println("dao.add()");
        }
    }
    package cn.anotation;
    
    import javax.annotation.Resource;
    
    import org.springframework.stereotype.Service;
    
    @Service(value="userService")
    public class UserService {
    
        //定义成员属性,无需set()方法,直接对属性加注解即可
        //@Autowired
        @Resource(name="userDao")
        private UserDao dao;
        public void add(){
            System.out.println("service.add()");
            dao.add();
        }
    }

      做个简单的测试如下:(请勿将测试类名命名为Test)

    package cn.anotation;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Test01 {
    
        //注解创建对象
        @Test
        public void test(){
            //加载spring核心配置文件
            ApplicationContext context = 
                    new ClassPathXmlApplicationContext("bean1.xml");
            //得到配置创建的对象
            User user =(User)context.getBean("user");
            user.add();
        }
        //注解注入属性
        @Test
        public void test02(){
            //加载spring核心配置文件
            ApplicationContext context = 
                    new ClassPathXmlApplicationContext("bean1.xml");
            //得到配置创建的对象
            UserService service =(UserService)context.getBean("userService");
            service.add();
        }
        
    }

      配置文件和注解混合使用:
        一般创建对象使用配置文件
        属性注入使用注解的方式
        案例cn.xmlandano包下,使用bean2.xml配置文件
        步骤也是建立类,创建对象使用xml,而属性注入使用注解

      采用service中注入两个dao,三个类和配置文件如下:

      BookDao:

    package cn.xmlandano;
    
    public class BookDao {
    
        public void buy(){
            System.out.println("dao.buy()");
        }
    }

      OrderDao:

    package cn.xmlandano;
    
    public class OrderDao {
    
        public void buy(){
            System.out.println("order.buy()");
        }
    }

      BookService:

    package cn.xmlandano;
    
    import javax.annotation.Resource;
    
    public class BookService {
    
        //使用注解注入属性
        @Resource(name="bookDao")
        private BookDao bookDao;
        @Resource(name="oderDao")
        private OrderDao orderDao;
        public void buy(){
            System.out.println("service.buy()");
            bookDao.buy();
            orderDao.buy();
        }
    }

      配置文件:

    <?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> 
    
        <!-- 开启注解扫描 ,scan就是扫描之意-->
        <context:component-scan base-package="cn"></context:component-scan>
        <!-- 创建对象 -->
        <bean id="bookService" class="cn.xmlandano.BookService"></bean>
        <bean id="bookDao" class="cn.xmlandano.BookDao"></bean>
        <bean id="oderDao" class="cn.xmlandano.OrderDao"></bean>
    </beans>

        测试类与上一个测试类类似,不再赘述。

      【更新】 @Configuration 和@Bean的使用 :

        用于定义一个配置类,用@Configuration注解该类,等价 与XML中配置beans;用@Bean标注方法等价于XML中配置bean。

        具体请参见网友博文:http://blog.csdn.net/vvhesj/article/details/47661001

    二、AOP    

      1)AOP概述
      2)AOP底层原理
      3)AOP相关术语
      4)AOP操作  

      1)AOP概述:面向切面编程
        struts2中的第一个浅的层面:不修改源代码拓展功能
        AOP采取横向抽取机制,取代传统的纵向继承体系的重复性代码——抽取横切关注点

    代码混乱:越来越多的非业务需求(日志和验证等)加入后, 原有的业务方法急剧膨胀. 每个方法在处理核心逻辑的同时还必须兼顾其他多个关注点.

    代码分散: 以日志需求为例, 只是为了满足这个单一需求, 就不得不在多个模块(方法)里多次重复相同的日志代码. 如果日志需求发生变化, 必须修改所有模块.

      2)AOP底层原理:
        例如有一个这样的Service的类和add()方法

    public class UserService(){
                    public void add(){
                        //添加用户逻辑
                    }
                }

        现在想要为add()方法添加日志功能,需要在add()方法内部加入实现代码;
        如果有很多方法(update.delete...)等很多方法都要重复性加,不可取!

        第一阶段的纵向的解决方案是新建一个类UserLog 定义实现log()日志方法;
        其它类想要实现采用继承UserLog类
        UserService extends UserLog
        于是可以调用父类的方法实现日志功能:super.log();
        缺陷是例如方法名称发生了变化,则所有的子类的调用代码都要修改

        AOP解决方案是横向抽取机制(底层动态代理)
        动态代理做的主要的事情是进行方法的增强;
      第一种情况:有接口的情况,使用jdk动态代理
        使用动态代理创建接口实现类和代理对象,例如有一个interface Dao 和 class DaoImpl implements Dao
        创建一个和DaoImpl平级的对象,但是这个不是真正的对象而是一个代理对象,但代理对象和原对象有相同的功能
      第二种情况:没有接口的情况,cglib动态代理
        创建User类的子类的代理对象,在子类中可以调用父类的方法完成增强 

      3)AOP相关术语(重点掌握的为带*的)
        Joinpoint:连接点
            类里面哪些方法可以被增强,这些方法就称为连接点
        *Pointcut:切入点
            在类中可以有很多方法被增强,在实际操作中只增强了部分方法,实际增强的方法就称为切入点
        *advice:通知/增强
            例如之前想要加log()日志功能,这个日志功能就叫增强,也就是实际拓展的功能的逻辑
            通知/增强分为:例如要增强add()方法
          前置通知:在方法之前
          后置通知:在方法之后(无论方法是否出现异常),执行结果在最终通知厘米
          异常通知:在出现异常后通知(用的少)
          最终通知:在后置之后执行
          环绕通知:方法前、后都要执行(例如计算方法执行时间)

        【更新】 前置通知[Before advice]:在连接点前面执行,前置通知不会影响连接点的执行,除非此处抛出异常。 
            正常返回通知[After returning advice]:在连接点正常执行完成后执行,如果连接点抛出异常,则不会执行。 
            异常返回通知[After throwing advice]:在连接点抛出异常后执行。 可以使用异常作为入参,当出现指定异常时才执行(add(NullPointException ex))
            返回通知[After (finally) advice]:在连接点执行完成后执行,不管是正常执行完成,还是抛出异常,都会执行返回通知中的内容。 
            环绕通知[Around advice]:环绕通知围绕在连接点前后,比如一个方法调用的前后。这是最强大的通知类型,能在方法调用前后自定义一些操作。环绕通知还需要负责决定是继续处理join point(调用ProceedingJoinPoint的proceed方法)还是中断执行。

          需要带ProcedingJointPoint参数(功能最强大,但却不代表最常用) ,类似于动态代理,并且必须有返回值,返回值为目标方法返回值

    环绕的前置后置返回异常通知都可以做

        切面优先级:如果有多个切面,优先级如何确定呢?可以在增强的类上(切面)使用@Order注解,其中的参数值越小优先级越高,例如@Order(1)


        *Aspect:切面
            把增强应用到切入点的过程,这个过程就叫切面。例如在add()方法上增强一个日志功能

      【更新】AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。
        剩下几个作了解:
            引介,目标对象,织入(把增强应用到类的过程),代理

      【更新】:慕课网友测试的顺序:

            经测试,当有配置around时,after和after-returning的执行顺序和它们在xml文件中配置顺序相反。

      4)spring的AOP操作(达到会用):
        使用AspectJ进行AOP操作,AspectJ是一个面向切面的框架,springAOP本身是为了提供整合
        经常被用来和spring一起使用来进行AOP的操作,虽然它本身不是spring的一部分

      使用aspectJ实现AOP操作主要有两种方式:
        1.基于aspectJ的xml配置
        2.基于aspectJ的注解实现(day03补充),更方便,更简洁

      AOP操作准备工作:
      导入AOP相关的jar包
        aop asp.. spring-asp spring-aop详见spring02项目(此时有如图10个jar包了)

               


      创建核心配置文件,导入AOP的约束(bean3.xml)找约束不再赘述

      实际操作:
        建两个类:基础的类Book 增强的类 BuyBook

    package cn.aop;
    
    //AOP操作
    public class Book {
    
        public void buy(){
            System.out.println("买书");
        }
    }
    package cn.aop;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    
    //增强book类
    public class BuyBook {
    
        public void before1(){
            System.out.println("买书前");
        }
        //环绕通知可以使用一个参数
        public void around(ProceedingJoinPoint pj) throws Throwable{
            
            System.out.println("方法之前-环绕");
            
            //执行被增强的方法
            pj.proceed();
            
            System.out.println("方法之后-环绕");
        }
    }

        //这里也可以给环绕通知配置另外的和被增强方法的参数列表,来取得被增强方法的参数

      使用表达式配置来配置切入点(增强的方法),也可以在配置文件中配置order等和注解一样的操作

    bean3.xml:

    <?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-4.3.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> 
        <!-- 配置对象 -->
        <bean id="book" class="cn.aop.Book"></bean>
        <bean id="buyBook" class="cn.aop.BuyBook"></bean>
        <!-- 配置aop操作 -->
        <aop:config>
            <!-- 1.配置切入点,express为配置表达式 id为切入点的名字 -->
            <aop:pointcut expression="execution(* cn.aop.Book.buy(..))" id="pointcut1"/>
            <!-- 2.配置切面(增强的过程) ref属性为增强的对象-->
            <aop:aspect ref="buyBook">
                <!-- 前置增强 ,method指定增强类的哪个方法作为前置增强,pointcut-ref为要增强的切入点-->
                <aop:before method="before1" pointcut-ref="pointcut1"/>
                <!-- 环绕通知 -->
                <aop:around method="around" pointcut-ref="pointcut1"/>
            </aop:aspect>
        </aop:config>
    </beans>

      常用AOP表达式:
        格式:execution(<访问修饰符>?<返回值><方法名>(参数)<异常>)——方法签名
        一般 访问修饰符写 *
        后面接方法的全路径
      例如:注意的点:可以使用通配符, *后加空格以示区分
      注意参数里是..两个点,表示参数包含在里面
      execution(* cn.aop.Book.add(..)) ——易知这里 * 代表任意修饰符和任意返回值,后面代表任意参数(具体写的话可以写 add(int, int))
      execution(* cn.aop.Book.*)
      匹配所有以save开头的方法
      execution(* save*(..))

       【更新】 切入点表达式可以使用 && ||等进行组合连接的运算(但是不建议)

        完整AOP表达式语法,参考http://blog.csdn.net/qq525099302/article/details/53996344

      表达式重用:PointCut的定义(适用于注解方式)

      当然,有时候我们知道切入点表达式是可以重用的,那么如何重用呢?可以通过定义一个专门的方法,示例既相关说明如下(如果不在相同包,需要指定包名):

    /**
         * 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码. 
         * 使用 @Pointcut 来声明切入点表达式. 
         * 后面的其他通知直接使用方法名来引用当前的切入点表达式. 
         */
        @Pointcut("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(..))")
        public void declareJointPointExpression(){}
        
        /**
         * 在 com.atguigu.spring.aop.ArithmeticCalculator 接口的每一个实现类的每一个方法开始之前执行一段代码
         */
        @Before("declareJointPointExpression()")
        public void beforeMethod(JoinPoint joinPoint){
            String methodName = joinPoint.getSignature().getName();
            Object [] args = joinPoint.getArgs();
            
            System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
        }

        具体配置及解释见bean3.xml
          配置略显麻烦,后期引入注解形式!
      后置通知类似不再赘述
      环绕通知:
      比较特别的是增强方法的写法(注意一个参数),配置文件同理

      作个简单测试:

    package cn.aop;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Test03 {
    
        @Test
        public void test(){
            //加载spring核心配置文件
                    ApplicationContext context = 
                            new ClassPathXmlApplicationContext("bean3.xml");
                    //得到配置创建的对象
                    Book book =(Book)context.getBean("book");
                    book.buy();
        }
    }

    【更新】(引自慕课评论):我听了两个小时,终于听懂了老师要讲的东西。让我来说一下这节课的内容:Introductions Advice简介通知。简介通知的主要配置是:声明一个接口A,再声明一个实现接口A的实现类B,并且用types-matching指定当前的这个简介通知所关联的业务类。然后通过implement-interface属性把接口A强制作为这些业务类的父类。在单元测试中,用getBean(beanId)方法得到业务类,然后把这个业务类强制转换成接口A类型,然后调用接口A的实现类B的方法。简介通知的用途我认为是:在业务逻辑操作中将横向的执行顺序改变为纵向的处理日志,事务相关的服务,通过类B实现这些服务的具体实现

    三、log4j介绍
      (之前一直有警告)
      通过log4j可以看到程序运行过程中更详尽的信息
      (哪些对象被创建了,什么配置文件被修改了以及一些错误信息等等)

      使用的步骤:
        导包(之前已导)
        复制log4j的配置到src下(只需看懂,无需手写)
      之前没加配置文件它就不知道该以什么样的格式输出
        rootLogger 日志级别
          INFO 基本信息
          DEBUG 详细信息
        //log4j待补充

    这里贴出一个log4j.properties简单示例(注意修改一些例如路径等参数):

    # Set root logger level to WARN and append to stdout
    log4j.rootLogger=WARN, stdout, error
    #WARNu4E3Alogu8F93u51FAu7EA7u522BuFF0CstdoutuFF0Cerroru4E3Au8BE5logu7684u522Bu540DuFF0Cu4E0Bu9762u5C06u7528u5230
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    
    # Pattern to output the caller's file name and line number.
    log4j.appender.stdout.layout.ConversionPattern=%d %5p (%c:%L) - %m%n
    
    # Print only messages of level ERROR or above in the package noModule.
    log4j.logger.noModule=FATAL
    
    # OpenSymphony Stuff
    log4j.logger.com.opensymphony=INFO
    log4j.logger.com.opensymphony.webwork=DEBUG
    
    # Spring Stuff
    log4j.logger.org.springframework=INFO
    
    #################################
    #                       u9519u8BEFu4FE1u606F #
    #################################
    log4j.appender.error=org.apache.log4j.DailyRollingFileAppender
    log4j.appender.error.File=F:/errors.log
    log4j.appender.error.layout=org.apache.log4j.PatternLayout
    log4j.appender.error.layout.ConversionPattern=[%d]-%-5p (%F:%L)|%m%n
    log4j.appender.error.DatePattern='.'yyyy-MM-dd
    log4j.appender.error.Threshold=ERROR
    
    ###################################
    #                       CONSOLE #
    #################################
    
    log4j.appender.console=org.apache.log4j.ConsoleAppender
    log4j.appender.console.layout=org.apache.log4j.PatternLayout
    log4j.appender.console.layout.ConversionPattern=(%F:%L)|%m%n
    
    log4j.appender.errorcsle=org.apache.log4j.ConsoleAppender
    log4j.appender.errorcsle.layout=org.apache.log4j.PatternLayout
    log4j.appender.errorcsle.layout.ConversionPattern=%-5p (%F:%L)|%m%n
    log4j.appender.errorcsle.Threshold=ERROR
    
    ##################################
    #                       u4E1Au52A1u7CFBu7EDF #
    #################################
    log4j.logger.cn.vesung=DEBUG, logic
    
    log4j.appender.logic=org.apache.log4j.DailyRollingFileAppender
    log4j.appender.logic.File=F:/logic.log
    log4j.appender.logic.layout=org.apache.log4j.PatternLayout
    log4j.appender.logic.layout.ConversionPattern=[%d]-%-5p (%F:%L)|%m%n
    log4j.appender.logic.DatePattern='.'yyyy-MM-dd

    //Log4j的详细介绍请见另一篇博客

    四、Spring整合web项目
      在action里调service,service里调dao;
      service里调dao可以用属性注入;
      在spring02_web里演示
        导包:
      先导入struts2和spring的jar包

            

      建立action(继承ActionSupport) service(依赖注入dao) dao

      在action中测试:action在struts.xml中配置(一定要配置过滤器!!!,不然404)

    目录结构如下:

      

    UserDao:

    package cn.dao;
    
    public class UserDao {
    
        public void add(){
            System.out.println("dao.add");
        }
    }

    UserService:

    package cn.service;
    
    import cn.dao.UserDao;
    
    public class UserService {
    
        private UserDao dao;
        
        public void setDao(UserDao dao) {
            this.dao = dao;
        }
    
        public void add(){
            System.out.println("service.add");
            dao.add();
        }
    }

    UserAction:

    package cn.action;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import com.opensymphony.xwork2.ActionSupport;
    
    import cn.service.UserService;
    
    public class UserAction extends ActionSupport{
    
        @Override
        public String execute() throws Exception {
            //在action中测试
            ApplicationContext context = 
                    new ClassPathXmlApplicationContext("bean1.xml");
            UserService service = (UserService) context.getBean("userService");
            service.add();
            return NONE;
        }
    }

    struts.xml配置如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
        "http://struts.apache.org/dtds/struts-2.3.dtd">
    <struts>
        <package name="demo01" extends="struts-default" namespace="/">
            <action name="userAction" class="cn.action.UserAction">
            </action>
        </package>
    </struts>  

    spring的bean1.xml配置如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
    
        <!-- 创建service -->
       <bean id="userService" class="cn.service.UserService">
               <!-- 注入dao -->
               <property name="dao" ref="dao"></property>
       </bean>
       <!-- 创建dao -->
       <bean id="dao" class="cn.dao.UserDao"></bean>
    </beans>

    会出现一个小问题,通过log4j可以看到,每次访问都要 创建对象加载文件等
    可以通过在服务器启动时就加载完成,把服务器给压力
    整合的基本原理第一天已有介绍这里直接贴过来:

    这个问题spring已经给我们封装好了

    ===spring整合web项目
    Hibernate时有一个遗留问题:sessionFactory的创建会比较慢,可以交给服务器来创建
    还有上面的bean1.xml每次都要加载spring核心配置文件,会影响性能
    以上的类似问题,解决的实现思想都是:
    把加载配置文件和创建对象在服务器启动时就创建,把压力给服务器
    spring中封装了相关的处理类,这里简单介绍原理:

    web阶段中有一个与天地同寿的对象 ServletContext
    它的创建可以由监听器进行监听
    在服务器启动的时候,服务器会为每个项目创建一个独一无二的对象:ServletContext
    可以使用监听器对ServletContext进行监听,可以知道对象的创建时间
    于是可以在监听器监听到ServletContext创建后
    加载spring配置文件,把配置文件中配置的对象进行创建
    对象创建后将创建的对象放在ServletContext中(它也是一个域对象)
    域对象的存取数据直接使用get/setAttribute()即可


    一个监听器,只需要配置这个监听器即可
    在web.xml中,使用listener配置:
    要整合web项目,还要一些整合的jar包:spring-web的包
    <!-- 配置监听器 -->
    <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    这里需要指定加载spring配置文件的位置
    (不然会去默认的路径下找默认的文件)WEB-INF/applicatonContext.xml
    <!-- 指定spring配置文件的位置 -->
    <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:bean1.xml</param-value>
    </context-param>
    这个参数名称去监听器的父类的常量中找

    这里贴出web.xml的配置文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
      <display-name>spring02_web</display-name>
      <!-- 指定spring配置文件的位置 -->
      <context-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>classpath:bean1.xml</param-value>
      </context-param>
      <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
      </filter>
      <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
      </filter-mapping>
      <!-- 配置监听器 -->
      <listener>
          <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
      <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>default.html</welcome-file>
        <welcome-file>default.htm</welcome-file>
        <welcome-file>default.jsp</welcome-file>
      </welcome-file-list>
    </web-app>
  • 相关阅读:
    安装nginx后启动提示缺少libjemalloc.so.2
    页面刷新后保持滚动条的位置
    mysql的tinyint字段返回布true / false的问题
    MySql处理死锁的解决方案
    apidoc使用记录
    微信公众号开发图片上传案例
    [ Error 分析] Comparison method violates its general contract!
    [intellij]create gradle project
    [重构]读书笔记
    [设计模式]迭代子模式 Iterator
  • 原文地址:https://www.cnblogs.com/jiangbei/p/6791516.html
Copyright © 2011-2022 走看看