zoukankan      html  css  js  c++  java
  • Spring-day02

    Annotation复习:
    1,Annotation:作为类型的元数据;
      1,给类型加标记;
      2,annotation可以添加各种类型的属性;
    2,Annotation的上的标记:
      1),target:标签可以加在什么类型上的;
        1),TYPE:所有类和接口;
        2),METHOD:方法;
        3),FIELD:字段上面;
        4),PARAMETER:方法的参数上面;
        5),CONSTRUCTOR:构造器方法;
      2),retention:把标签保留到什么时候;
        1),CLASS:保留到字节码;
        2),SOURCE:保留到代码;
        3),RUNTIME:保留到运行时;

    3,标签一般需要三方参与:
      1),标签类型;
      2),打标签的目标类型;
      3),解析程序;
      4),注解的参数:
        1),注解中属性的添加方式;
        2),注解中属性的默认值;
        3),getAnnotation方法的使用;

    Spring的注解使用;

    DI注解的使用:
    1,使用DI注解,在spring的配置文件里面还是需要配置bean本身;
    2,@Autowired
      1),autowired标签可以把需要的对象自动的注入到目标对象中;
      2),autowired标签可以放在字段上,也可以放在setter方法上面;
      3),autowired的执行流程:
        1),加载和解析XML文件;
        2),实例化所有的bean,并且方到spring的容器当中;
        3),解析对象的类型,如果对象类型的某些字段或者setter方法上面有@Autowired标签,
          1),在容器中找到对应的对象;
          2),把找到的对象设置到字段或者setter方法中;
      4),怎么找对象?
        1),首先按照类型找;
        2),如果没有找到;系统报错(默认情况下,autowired标签有一个@Required标签的作用)
        3),如果找到多个,按照字段或者属性的名字,再用名字+类型去找;
        4),如果1,3都没有找到,报错;
        5),可以通过设置autowired的required=false让这个属性(字段)可以没有值;
        6),可以通过在字段或者setter方法上添加@Qualifier来规定我要注入的bean的名字;
        7),可以在Spring的主要对象上面添加@Autowired,让spring自动注入;
        8),所以,我们的spring的测试类,可以直接引用需要测试的目标对象就可以了;

    3,@Resource
      1),也可以通过resource标签来自动注入;
      2),怎么找对象?
        1),首先按照字段或者属性的名字找;
        2),如果找不到,报错(resource关联的对象必须存在)
        3),如果按照名字找不到,再按照类型来找;
        4),可以通过resource的name属性来指定一个bean注入;如果一旦设置了resource的name属性,就只能按照名字找了;
        5),因为resource是javax的标签,所以resource不能注入spring中关键的对象;

    选择:
    1,Resource是JavaEE的规范,但是缺少spring对autowired标签的一些加强;
    2,一般我们选择使用@Autowired标签;

    使用IoC注解:

    1,在配置文件中告诉spring去哪里扫描实例对象;

        <!-- 
            如果要使用IOC的注解,一定要配置一个context:component-scan,
            base-package表示,让Spring去扫描包及其子包下的类(是否带有IOC注解)
            如果要配置多个包,包之间用逗号隔开
         -->
        <context:component-scan base-package="com.rk1632._03_annotation_ioc"/>

    2,在类型上面添加anntation:
      1),@Component:一般我们写在工具类上面;
      2),@Service:一般我们写在服务对象上;
      3),@Repository:一般写在DAO上面;
      4),@Controller:一般写在Action上面;并且在SpringMVC中,Controller就在标示一个Controller对象;
    3,这四个标签对于spring来说,作用相同,主要是给程序员看的;

    <bean id="" class="" factory-method="" factory-bean="" init-method="" destory-method="" scope=""/>
    1,id:所有的四个标签都支持value值,这个值就是id的名字;
    2,class:不用写了;
    3,factory-method和factory-bean就没有对应的标签了;如果有这种需要,直接配置到XML里面(XML和Annocation是可以混用的)
    4,init-method:使用@PostConstruct直接放在init方法上面;注意:initmethod一定是在DI(依赖注入)之后执行的;
    5,destory-method:使用@PreDestroy直接放在destory方法上面;
    6,scope,在类上面添加@Scope("prototype");

    代理模式

    提出问题:
    问题:事务是开在dao上面的,所以如果在service中调用多个dao的方法,如果在这些方法执行过程中报错,事务没法回滚

    对于我们的应用,事务都应该开在service的方法上面

    解决办法:
    1,sessionFactory.getCurrentSession:得到当前线程中的session;
    2,把session的开启和事务的开启,事务的提交从dao中移到service方法中

    存在一个事务跨层的问题,但是如果直接把事务管理的代码写在service中,会造成service代码污染,最好的方式就是把事务的代码从service中抽取出来,使用代理模式,再加到目标代码中:

    静态代理:

    public class EmployeeServiceImpl implements IEmployeeService {
    
        public void save(Employee e) {
            
            System.out.println("save Employee");
        }
    
        public void update(Employee e) {
            
            System.out.println("update Employee");
        }
    
    }
    public class EmployeeTranscationServiceImpl implements IEmployeeService {
    
        private IEmployeeService target;
        
        public EmployeeTranscationServiceImpl(IEmployeeService service){
            this.target = service;
        }
        
        @Override
        public void save(Employee e) {
            System.out.println("sessionFactory.getCurrentSession");
            System.out.println("session.getTranscation().begin()");
            target.save(e);
            System.out.println("session.getTranscation().commit()");
        }
    
        @Override
        public void update(Employee e) {
            System.out.println("sessionFactory.getCurrentSession");
            System.out.println("session.getTranscation().begin()");
            target.update(e);
            System.out.println("session.getTranscation().commit()");
        }
    
    }

     

    静态代理分析:

    静态代理分析:
    1,静态代理确实处理了代码污染的问题;

    问题:
    1,重复的代码仍然分散在了各个方法中;
    2,需要为每一个真实对象写一个代理对象;

    Struts中的拦截器:

    动态代理

    1,有接口的对象的动态代理
      1),JDK中提供了针对有接口对象的动态代理实现方式;
    2,没有接口的对象的动态代理
      1),使用继承的方式完成动态代理;javassist/cglib

    1,客户端调用save方法,执行被InvocationHanalder拦截;
    2,在invoke方法中执行添加的额外的逻辑;
    3,调用m.invoke(target,args)执行到了真实的目标对象上的对应方法;
    4,真实对象返回执行结果到InvocationHandler里面;
    5,执行完成其他逻辑,把结果返回给客户端;

    使用JDK的动态代理流程:
    1,使用JDK的动态代理,必须要保证代理的真实对象是有接口的;

        /**
         * proxy:代理出来的对象
         * method:这次调用的方法
         * args:这次调用方法传入的参数
         */
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            }

    2,需要实现一个InvocationHandler,并实现invoke方法(如上);
      0),需要给InvocationHanlder的实现类提供真实对象;
      1),在invoke方法中执行自己的额外添加的业务逻辑;
      2),在编写完额外逻辑之后,调用method.invoke(target,args)执行真实对象上的对应方法;
      3),得到真实方法执行的结果;
      4),处理结果,并把结果返回给客户端;

            //Proxy:专门用来生产代理类的对象
            //newProxyInstance:需要三个参数
            //1,ClassLoader():专门用来加载类的
            //2,interfaces:需要接口的数组,这个接口就是我们目标对象实现的接口
            //3,处理动态代理的类的对象
           IEmployeeService service = (IEmployeeService)Proxy.newProxyInstance(service.getClass().getClassLoader(), 
                            new Class[]{IEmployeeService.class}, 
                            new TranscationInvocationHandler(service,txManager));

    3,在客户端调用Proxy.newProxyInstance方法(如上):
      1),需要传入目标对象的classloader;
      2),需要传入目标对象的接口类型;
      3),需要传入一个自定义的InvocationHandler的实例;

    使用CGLIB完成没有接口的对象的动态代理

    public class LogInvocationHandler implements InvocationHandler {
    
        private Object target;
        
        public LogInvocationHandler(Object target){
            this.target = target;
        }
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            System.out.println("操作日志:操作时间:"+new Date()+", 当前调用的方法:"+method.getName()+", 传入的参数:"+Arrays.toString(args));
            return method.invoke(target, args);
        }
    
    }
        @Test
        public void test() throws Exception {
            SomeBean bean = new SomeBean();
            
            //增强器
            Enhancer enhancer = new Enhancer();
            //设置目标对象的ClassLoader
            enhancer.setClassLoader(bean.getClass().getClassLoader());
            //设置这个动态代理的父类
            enhancer.setSuperclass(SomeBean.class);
            //设置要传入的拦截器
            enhancer.setCallback(new LogInvocationHandler(bean));
            //使用create方法创建代理对象
            SomeBean o = (SomeBean) enhancer.create();
            o.print1();
        }

    小结:
    1,动态代理处理:重复的代码仍然分散在了各个方法中这个问题;
    2,动态代理需要调用代码为每一个类去创建动态代理对象;
    3,动态代理最小的单位就是一个类;
    4,如果只想处理一个类型中的某些方法,在invoke方法中根据当前执行的方法名称去做判断就可以了;

    Spring的AOP:

    AOP就是一种更高级的动态代理的使用;
    Aspect Oritention Programming(面向切面编程)

    切入点:要加入业务逻辑的点(在哪些类的哪些方法上面;)
    通知:通知包含两个方面,1,代表方法的执行时间,2,在这个时间上面要做什么事情;
    切面:一个切入点+一个通知=一个切面(在什么地方,在什么时候,做什么事情);
    织入:把一个切面应用到真实对象上面的过程,就叫做织入;

    在Java中,没有语言能够准确的描述切入点;所以,有一个AspectJ,这是一种语言,提供了用于描述切入点的语言;

    使用Spring的AOP:
    1,使用XML的配置方式;
    2,使用Annotation的配置方式;

    定义切入点的表达式

    如果可以是任意的值,使用*就可以了;

    定义切入点表达式的例子如下:

    aop包中,所有的service的所有的方法;
    execution(* com._520it.spring.day2.aop.*Service.*(..));

    day2中所有service的save方法;
    execution(* com._520it.spring.day2..*Service.save(..))

        <!-- 配置AOP -->
        <aop:config>
            <!-- 配置一个切入点
                expression:这个切入点的表达式
                id:这个切入点的名称
             -->
            <aop:pointcut expression="execution(* com.rk1632._08_aop.*Service.*(..))" id="pc"/>
            <!-- 配置一个切面 
                ref:代表在这个切面的定义中,所有的做什么事情,都是由txManager这个对象的方法提供的
            -->
            <aop:aspect ref="txManager">
                <!-- before代表方法执行之前 -->
                <aop:before method="begin" pointcut-ref="pc"/>
                <!-- after-returning代表方法正常执行之后 -->
                <aop:after-returning method="commit" pointcut-ref="pc"/>
                <!-- after-throwing代表方法抛出异常的时候 -->
                <aop:after-throwing method="rollback" pointcut-ref="pc"/>
            </aop:aspect>
        </aop:config>

    Spring中AOP的通知类型:
    aop:before:前置通知;
    aop:after-returning:后置通知;
    aop:after:最终通知;
    aop:after-throwing:异常通知;
    aop:around:环绕通知;

    小结:
    1,准备了真实的对象,准备一个类,这个类里面的方法用来提供通知里面的做什么;

    2,这两个类都要配置到Spring容器中;

    3,配置springAOP
      1,引入aop命名空间;
      <aop:config>
        <aop:point-cut expression="" id="" />
        <aop:aspect ref="">
          <aop:before method="" pointcut-ref="">
          <aop:after-returning method="" pointcut-ref="">
          <aop:after-throwing method="" pointcut-ref="">
        </aop:aspect>
      </aop:config>

    4,我们在客户端还是直接注入的是在spring里面配置的真实对象;
    实际上,spring在为这些对象创建好代理对象之后,会使用这些创建好的代理对象去替换容器中的原始对象;

    SpringAOP的执行流程:
    1,解析xml;
    2,实例化所有的bean;
    3,解析aop:config;
      1),解析aop:aspect,得到aspect引用的对象;txManager
      2),解析aop:aspect里面的每一个切面;
        1),得到该aspect对应的pointcut-ref;
        2),得到pointcut-ref对应的pointcut的表达式;
        3),使用表达式中用于匹配类型的表达式;
        4),使用该表达式去和spring里面配置的所有的bean的类型进行匹配;
          1),如果匹配不上,不管;
          2),如果匹配上了,该对象作为spring动态代理的目标对象;
            1),如果该对象实现了接口,使用JDK的动态代理包装;
            2),如果该对象没有实现接口,使用cglib包装;
            3),得到配置的拦截时机+逻辑提供类(txManager)的对应方法(从method解析)+pointcut表达式中方法的匹配模式创建一个拦截器
            4),在把该拦截器使用对应的动态代理机制代理成代理对象;
            5),替换spring容器中的对应bean的实例;

  • 相关阅读:
    厦门大学 ACM 1465 连续数列 三分
    厦门大学 ACM 1437 三分
    南京理工 ACM
    厦门大学 ACM 1466 线段树维护
    LCS N(log (N) )
    hdu 1520
    HDU 2196
    zoj 3710 暴力
    互联网创业盈利模式指南(转)
    map
  • 原文地址:https://www.cnblogs.com/Java0120/p/9954652.html
Copyright © 2011-2022 走看看