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总结

原文地址:https://www.cnblogs.com/katy0308/p/13781340.html