zoukankan      html  css  js  c++  java
  • Sping整理(一)

    why spring

    1.可解决大量的重复创建对象的过程,将对象的创建交给spring。

    2.创建对象方式:
    1.直接new
    2.通过反射 Class class = Class.forName("com.xxx.xxx")
    class.newInstance().var

    spring 工厂模式

    1.工厂模式出现原因:解耦合,由工厂帮助创建对象

    2.原理:

    • 配置applicationContext.xml文件,在文件当中定义需要被工厂进行管理的bean。

    • 创建对象 ApplicationContext

      ApplicationContext:接口(本质)
      对应的实现类:1.非web环境下(main/junit):classPathXmlApplicationContext
      2.web环境下:XmlWebApplicationContext
      线程安全,可以多线程并发访问

    获取有需要使用的bean对象

    相关方法
    getBean()

    ​ 方式一:直接通过id
    ​ UserService userServiceImpl = (UserService) applicationContext.getBean("userService");
    ​ 方式二:指定id和类型,避免强制转换
    ​ UserService userService1 = applicationContext.getBean("userService", UserService.class);
    ​ 方式三:只通过类型,该类型必须只有一个bean
    ​ UserService userService2 = applicationContext.getBean(UserService.class);
    getBeanDefinitionNames()
    ​ 获取xml定义的bean的id数组
    ​ String[] names = applicationContext.getBeanDefinitionNames();
    getBeanNamesForType()
    ​ 根据类型获得bean的id数组
    ​ String[] names = applicationContext.getBeanNamesForType(UserService.class);
    containsBeanDefinition()

    ​ 是否包含bean的定义,只能判断id,不能判断别名name
    ​ boolean userService = applicationContext.containsBeanDefinition("userService");
    3.基础实现原理:
    ​ 通过配置文件获得对象的class文件,再反射调用无参构造进行创建,私有的无参构造也可以。

    ps:实体类对象不用spring创建,因为需要数据--->持久层创建

    spring注入

    1.概念:通过spring工厂及配置文件,对bean对象的成员属性进行赋值。

    2.价值:一般赋值方式:get/set ---->存在耦合 注入:解耦合,但还是需要get.set方法

    3.方式:

    <bean id="student" class="com.boss.leraning.springdemo.bean.Student">
        <property name="age" value="18"></property>
    </bean>
    

    4.实现原理:

    调用get set 方法:对应着property里面的name 调用setName方法---------->所以也叫set注入

    set注入

    注入各种类型的成员变量方法

    1.注入的成员变量的类型多种,不单只有int 和string ------->用value赋值即可

    2.JDK提供的数据类型

    • 8种基本数据类型+String

      <value>xxxx</value>
      
    • 数组类型

      <list>
          <value> aa</value>
      </list>
      
    • set集合

      <set>
          <value></value>//不一定是value,只有set的泛型是int/string
          <ref  bean=""/>//自定义类型
      </set>
      
    • list集合

      <list>
          <value></value>//不一定是value,只有set的泛型是int/string
          <ref  bean=""/>//自定义类型
      </list>
      
    • map集合//键遍历,值遍历,键值遍历

      <map>
          <entry>
              <key>//键
                  <value>
                  </value>//键的值
              </key>
              <value></value>//值,不一定是value,看具体情况
          </entry>
      </map>
      
    • properties

      <props>
          <prop key="">value</prop>//value只能是字符串类型
      </props>
      

    3.用户自定义类型

    * 一个property标签调用一次set方式
    方式一:直接在bean内部定义另一个bean
    <bean id="useService "class=" ">
        <property name="useDao">//调用set
             <bean class="userdao"></bean>/创建对象
        </property>
    </bean>
    //方式二:当userDao需要被别的对象使用
    方式一问题:多次创建userDao浪费内存
    <bean id="userDao" class=""></bean>
    <bean id = "userService">
        <property name="useDao">
            <ref bean="userDao"/>
        </property>
    </bean>
    //简写方式:
    <bean id ="" class="">
        <property name="" value=""></property>
        <property name="" ref=""></property>
    </bean>
    

    构造注入

    1.通过有参构造方法对成员变量进行赋值--->定义有参构造函数

    2.方式

    <bean id="",class="">
        <constructor-arg>//一个该标签调用一次构造方法,传递一次参数
            <value></value>
        </constructor-arg>
    </bean>
    

    3.构造参数重载

    通过控制constructor标签的数量,找到对应的构造函数

    对于参数个数相同情况,要通过类型去区分,eg ,name和age
    否则会按顺序去构造
    <bean id="",class="">
        <constructor-arg type="int" ,value=""></constructor-arg>
    </bean>
    

    总结

    1.一般多数采用SET注入

    反转控制(ioc inverse of Control)

    1.控制的概念:对成员变量赋值的控制权-----代码/配置文件+Spring工厂

    2.反转控制:将控制权从代码转移到配置文件当中。

    依赖注入(dependency injection)

    1.依赖:使用到它,需要它----->将它作为成员变量,通过配置文件进行赋值注入

    复杂对象

    创建复杂对象的方式

    • FactoryBean接口:

      1.实现接口

      public class MyFactoryBean  implments FactoryBean<欲创建的对象的泛型>()
      {
          //待重写的方法
          public Object getObject(){
              //用于书写创建复杂对象的代码,将其作为返回值返回
          }
          public class getObjectType(){
              //返回所创建对象的class对象
          }
          public boolean isSingleton(){
              //返回创建对象是否是单例模式
              //false,每次回创建新的复杂对象
          }
      }
      

      2.配置文件的配置

    <bean id="conn" class="MyFactoryBean"></bean>
    //通过getBean("conn")获取到的不是MyFactoryBean,而是它所创建的复杂对象---connection
    //通过getBean("&conn")获取到是MyFactoryBean
    

    ​ 3.依赖注入例子:

    4.实现原理:1.id+class---->获得ConectionFactoryBean----->通过getObject获得conn对象。

    • 实例工厂

      1.使用原因:(1)spring框架侵入避免:离开了spring无法进行获得对象。

      ​ (2)无法拿到ConnectFactoryBean的.java文件

      2.开发步骤:

      ​ (1)编写ConnectionFacctory---->编写获得conn对象的代码

      ​ (2)编写配置文件,让spring管理它

      <bean id="connectionFactory" class=""></bean>//配置了实例
      <bean id="conn" factory-bean="connectionFactory" factory-method="getConnection"></bean>
      
    • 静态工厂

      与实例工厂的区别就是工厂的方法是静态的。

      控制对象的创建次数

      1.复杂对象:isSingleton方法返回true/false

      2.简单对象:通过在bean注入的时候编写scope属性:singleton(默认值)/prototype

    创建一次的对象:

    1.sqlSessionFactory

    2.Dao

    3.UserService

    需要创建多次的对象:

    1.sqlSession

    2.Connection

    对象的生命周期

    对象的各个阶段

    • 创建阶段

    1.对于singleton对象

    在工厂创建的时候就进行创建
    懒初始化:在bean标签当中加入属性lazy-inti="true",也能达到prototype的效果
    

    2.prototype对象

    在对象被获取调用的时候,才被创建
    
    • 初始化阶段

      1.什么是初始化?
      数据库,IO,网络流等资源操作。应用一般比较少。
      由程序员编写初始化方法,经由spring进行调用进行初始化
      形式一:
      实现接口initializingBean接口当中的public void afterPropertiesSet()方法
      形式二:
      编写初始化方法,在<bean id="" class="" ,init-method="初始化方法名"/>
      //创建对象-->注入-->初始化
      
    • 销毁阶段

      工厂关闭时,spring调用对象的销毁方法。  ctx.close;
      销毁操作:释放资源的操作。
      //方式一:
      实现DisposableBean当中的public void destory()方法,该方法存在异常,可抛出
      //方式二:
      销毁方法由程序员进行编写,在<bean id="",class="" destory-method="销毁方法名">
      注意:
      销毁操作只对sigleton对象有用。
      

    配置文件参数化

    1.参数化的目地:能够方便后期的维护,将一些经常修改的值,定义到一个property文件当中。通过${kay名}获得值。

    例如:

    1.新建小配置文件:

    2.配置文件的内容:

    3.将小配置文件整合spring配置文件

    classpath:resouce 和同级的java文件夹下的内容的整合。

    自定义类型转换器

    1.在注入的时候将String转换为需要注入的属性的类型。

    2.在某些情况下Spring定义的类型转换器不满足我们的需求,例如将string类型转化为Date 类型。

    内置只支持---”yyyy/MM/dd"的格式

    -----------》进行自定义类型转换器

    • 方式:

      1.实现converter接口

      //泛型当中的第一个表示原始类型,第二个表示要转化类型
      public class MyConverter implements Converter<String,Date>(){
          @override
          public Date converter(String source)//source就是配置文件中代表日期的字符串
          {
              SimpleDateFormat sdf = new SimpleDateFormatImpl("yyyy-MM-dd");//存在异常,try-catch
              Date date= sdf.parse(source);
              return date;//转化后的返回值会被spring返回给待注入的对象--->接口回调
          }
      }
      

      2.添加到配置文件

      spring提供了一个类ConversiobServiceFactoryBean,将自己写的转换器赋值给这个类的属性converters

      该属性是set集合类型。该类命名中的id必须为conversionService

    后置处理Bean

    BeanPostProcessor(接口):对工厂创建的对象进行进一步的再加工

    待重写方法:
       Object postProcessBeforeInitiallization(Object bean String BeanName){
    //在创建完对象,待初始化之前进行调用
    return bean;//返回加工之后的对象
    }
    
     Object postProcessAfterInitiallization(Object bean String BeanName){
    //在初始化完成之后进行调用
    return bean;//返回加工之后的对象
    }
    //如果没有实现初始化操作的化,两个方法没有区别,但是不管有没有进行加工都要将bean对象进行返回。
    

    • 开发步骤:

      1.实现BeanPostProcessor

      image-20200727212903836

      2.配置文件

    <bean id ="" class=""></bean>
    

    注意 beanPostProcessor会对工厂的所有对象都进行加工,所以前面代码要进行类型判断,instanceof()

    代理设计模式

    1.javaEE的开发层次当中最重要的是service层

    dao--->service--->controller
    

    2.service的功能

    性能:业务的开始时间和结束时间之间的差。

    日志:谁,何时,做何事

    • service层到底需不需要写额外功能?

      调用者:需要,事务等处理功能。

      软件设计者:不需要,避免代码入侵。

      • 现实生活中例子

    1.代理设计模式概念:通过代理类(proxy)来完成目标类的额外功能,利于目标类的维护。改动额外功能时不用改变目标类

    2.

    • 与目标类相同的名字
    • 实现额外功能
    • 有目标类的接口实现

    以上代理方法为静态代理为每一个原始类都编写一个代理。

    • 静态代理缺点:

      1.代理类数量过多,不利于项目管理。

      2.额外功能维护性差。如果想改变日志的格式,就得所有的代理类都修改。

    spring 动态代理

    本质与静态代理相同

    1.导入相关的jar包

    • 开发步骤:

      1.创建原始对象

      编写原始对象---userService
      创建对象--->spring的bean注入
      

      2.额外功能

      实现接口MethodBeforeAdvice中的Before方法
      public class before implements MethodBeforeAdvice{
      	@override
      	public void bofore(Method method Object[] args  Object target )
      	//method:原始类中要增加额外功能的方法
      	//args:原始方法的参数
      	//target:额外功能所增加给的对象
      }
      在配置中进行bean注入
      <bean id="before" class="xxxx.MyMethodBeforeAdvice"></bean>
      

      3.定义切入点

      切入点:定义额外功能加入的位置。

      //通过定义切入点来指定你的Before方法需要加给那些方法
      * 通过配置文件进行定义:
      <aop:config>
      <aop:pointcut id=“pc” expression=“excution(* *(..))”/>//所以的方法都作为切入点
      </aop:config>
      
      

      4.组装

      <aop:config>
      <aop:pointcut id=“pc” expression=“excution(* *(..))”/>//所以的方法都作为切入点
      <aop:advisor advice-ref="before" pointcut-ref="pc">//组装,把切入点与before方法进行整合
      </aop:config>
      

      5.使用

      通过getBean(原始对象)就可以获得代理对象--->要用接口类型进行存储
      ApplicationContext ctx = new ClassPathXmlApplicationContext();
      UserService userService = (UserServiece)ctx.getBean();
      
      • spring创建的动态代理类在哪里?

        spring通过动态字节码技术在jvm里创建,在jvm使用结束后也消失。

    • 好处:

      1.简化代理的操作,额外功能只写一个额外功能类即可,然后加到方法前面

      2.提升代理类的维护性---->打开扩展,关闭修改。

      不想要这个额外功能,写个新的去绑定配置即可,省去旧的修改。

    方式二(推荐,可以在原始方法的前后都添加额外功能):
    实现接口MethodInterceptor中的invoke方法
    pucblic class Arround implement MethodInterceptor{
         @override
         //该方法的参数就是所要添加额外功能的原始方法
         public Object invoke(MethodInvocation invocation) Throws Throwsable{
             sout"------"//按自己要求添加额外功能
             Object ret= invocation.proceed();//表示让原始方法运行,以便让额外功能添加在其之后
              return ret;
         }
    }
    //后步骤与MethodBeforeAdvice相同
    该接口的功能比MethodBeforeAdvice更加强大,并且可以通过改变ret,改变返回值。
    
    • 何时在原始方法前后都添加额外功能?

      事务 :原始方法执行前开启事务,执行之后提交事务

      异常:在原始方法抛出异常的时候执行

    切入点详解

    方法切入点

    切入点的位置:决定额外功能加入的位置
    <aop:pointcut id="pc" expression="execution(* *(..))"></aop:pointcut>
    //切入点函数:execution()
    //切入点表达式:execution(* *(..))
    第一个* --->修饰符,返回值
    第二个*---->方法名
    ()中的..---->参数个数任意
    eg.
    * login(String)//方法login,参数一个,如果是非java.lang包中类型写全限定名
    * login(String,..)//表示只要第一个参数是String即可,后参数的个数和类型没要求
    
    • 精准的方法切入点的限定

      在不同的包下的方法可能完全相同

      解决方法:

    类切入点

    1.使用场景:对于某个类中的所有方法加入额外功能。

    2.方式:

    //所有包中的userServiceImpl类中的所有方法
    <aop:pointcut id="pc" expression="execution(* *..uerServiceImpl.*(..))"></aop:pointcut>
    

    包切入点

    定义在某个包下的所有的类的切入点:

    语法:
    //该包中的所有类的所有的方法
    <aop:pointcut id="pc" expression="execution(* com.xxx.xxxx.*.*(..))"></aop:pointcut>
    //该包及其子包下的所有类的所有方法,包后多加一个点
    <aop:pointcut id="pc" expression="execution(com.xxx.xxxx..*.*(..))></aop:pointcut>
    //更具有实战价值
    

    切入点函数

    1.作用:执行切入点表达式

    2.常用的切入点函数:

    execution:功能最为齐全,但是表达式书写麻烦
    ----->其他的函数进行简化,但是本质还是execution
    * args(String String)//关心函数的参数。有2个String参数的都满足
    * within(*..userSericeImpl)//用于类或包的切入点,表示所有的UserService这个类
      within(com.xxx.xxx..*)//包及其子包下的所有的类原表达式:execution(* com.xxx.xxx..*.*(..))
    * annotation(com.xxx.xxx.log)//具有特殊注解的方法,例如自定义注解log,内部写注解全限名
    

    切入点函数逻辑运算

    1.概念:讲多个切入点函数之间进行and或者or运算以满足更为复杂的需求。

    and
    案例:login 同时参数为2个String 
    execution(* login(..)) and args(String String)
    注意:与操作不能用于同种类型的切入点函数,不能execution and execution
    案例: login 和 register----->用and结果为空 ,应该用or操作
    
    or
    案例: login 和 register----->用and结果为空 ,应该用or操作
    execution(* login(..)) or execution(* register(..))
    

    AOP编程

    AOP概念

    本质就是Spring的动态代理,通过代理类为原始类增加额外功能。

    好处:利于原始对象的维护。

    AOP开发步骤

    1.原始对象

    2.额外功能

    3.切入点

    4.组装

    切面 :由具有相同性质所共同构成的面。

    AOP底层实现原理

    待思考的问题:

    1.aop如何帮我们创建动态代理对象?

    2.为何通过id值获得的是代理对象而不是原始对象?

    JDK动态代理

    1.复习代理创建的三要素

    • 原始对象
    • 额外功能
    • 代理对象与原始对象实现相同的接口

    2.Proxy.NewProxyInstance()方法参数的分析:


    3.具体编码实现

    public calss TestJDKProxy(){
        public static void main(String[] args)
        {
            //创建原始对象
            private UserService userService = new UserService();
            //通过jdk生产代理对象
           //通过匿名内部类实现接口
     InvocationHandler handler = new InvocationHandler(){
       @override
      public Object invoke(Object proxy , Mehtod method , Object[] args) Throws Throwable{
                //额外功能
          sout."-----Proxy-log-------"
          		//让原始方法运行
          		Object ret=method.invoke(userService,args);
           		retuen ret;  
                }
            }
           UserService userServiceProxy = (UserService)
         Proxy.NewProxyInstance(TestJDKProxy.class.getClassloader,userService.getCalss.getInterface(),handler)//强制转换获得UserService
        }
    }
    

    cglib动态代理

    1.适用情形:原始类没有实现接口,只一个原始类

    2.解决方法:继承原始类

    3.编码步骤:

    public class TestCglib(){
        public static void main(String [] args){
    		//创建原始对象
            private UserService userService;
            //Cglib包自带的方法
            Enhancer.SetClassLoader(TestCglib.class.getClassLoader())//设置创建代理对象时的类加载器,也是借的
            Enhancer.SetSuperClass(userService.getClass())//设置父类的class文件,在jdk当中使用的是接口,这里是直接目标类
            //用内部类实现MethodInterceptor接口,与spring当中的接口不是一个,所处的包位置不同
                MehodInterceptor interceptor = new MethodInterceptor(){
    			@override
                public Object interceptor(Object o, Method method,Object[] args,MethodProxy methodProxy) Throws Throwable{
                     sout."----cglib----log----"//实现额外功能
                         Object ret =method.invoke(userService,args);//执行原始类的方法
                    //方法执行的三要素:1.执行对象--->原始对象
                    //				 2.执行的方法  3.执行方法的参数
                    return ret;
                }        
            }
            Enhancer.SetCallBack(interceptor)//通过实现接口的内部类设置额外功能;
            UserService userServiceProxy = (UserService) Enhancer.create();
            //之后就可以使用代理对象userServiceProxy了
        }
        
    }
    

    spring如何创建代理对象

    1.aop如何帮我们创建动态代理对象?

    2.为何通过id值获得的是代理对象而不是原始对象?

    3.具体编码

    (1)编写原始对象,并配置bean

    (2)实现BeanPostProcessor这个接口,并将实现类配置bean

    基于注解的AOP编程

    1.基本步骤

    • 原始对象
    • 额外功能
    • 切入点
    • 组装切面

    区别:通过切面类定义额外功能和切入点

    @Aspect
    public class MyAspect {
        @Around("execution(* *.*(..))")//表示所有方法
        public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
    
            System.out.println("---------aspect----log-------");
            Object ret = joinPoint.proceed();
            return ret;
        }
    }
    

    在配置文件当中进行配置

    <bean id="userService" class="UserServiceImpl"/>
    <bean id="myAspect" class="MyAspect"/>
      <!--  开启aop注解编程,设置底层为cglib将proxy-target-class设置为true-->
        <aop:aspectj-autoproxy proxy-target-class="true"/>
    

    细节:切入点复用

    @Aspect
    public class MyAspect {
        //切入点的复用,避免对于各种around方法当中的切入点进行多次修改
        //eg.将所有的login要该成register就要2个around函数都修改
        @Pointcut("execution(* *.login(..))")
        public void myPointCut(){}//空的函数体
    
        @Around("myPointCut()")//直接写自定义的切入点函数名即可
        public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
    
            System.out.println("---------aspect1----log-------");
            Object ret = joinPoint.proceed();
            return ret;
        }
        @Around("myPointCut()")
        public Object around2(ProceedingJoinPoint joinPoint) throws Throwable {
    
            System.out.println("---------aspect2----log-------");
            Object ret = joinPoint.proceed();
            return ret;
        }
    }
    

    开发的时候可能存在的问题:

    AOP总结

  • 相关阅读:
    【01】markdown语法
    H5系列之地理位置(必知必会)
    【07】像使用命令行一样使用 GitHub URL
    【11】把 GitHub 当 CMS 用
    【01】在 issue 中创建 list
    【06】GitHub WiKi
    【05】project board
    7.10-11 visudo、sudo
    7.7-9 chage、chpasswd、su
    7.1 useradd:创建用户
  • 原文地址:https://www.cnblogs.com/katy0308/p/13781340.html
Copyright © 2011-2022 走看看