Spring 笔记

笔记笔记笔记笔记!!!!回顾Spring相关知识

Spring

1. Spring是什么

​ Spring框架是一个集众多设计模式于一身的开源的轻量级项目管理框架

​ Spring框架提供一个以简单的、统一的、高效的方式构造整个应用,并且可以将单层框架以最佳的组合揉和在一起建立一个连贯的体系。

2. Spring作用

​ Spring 框架用来管理[创建|使用|销毁]项目中的组件,由于spring 框架可以帮我们生产项目中组件对象,因此也习惯称spring是一个工厂|容器

组件: 项目中的service,dao,controller,都是项目中的组件

注意: spring框架通常不管理对实体类对象创建

3. Spring 配置文件

# 配置文件名称: 任意名称 
# 配置文件位置: 项目中根下任意位置  resources目录下为起始点,若有文件夹,那就是  xx/spring.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-3.2.xsd ">

<!--通过bean标签管理组件对象-->
    	id推荐写法:接口的首字母小写
    <bean id="userDAO" class="com.wbb.UserDAOImpl"></bean>
    </beans>

启动工厂,获取对象

public static void main(String[] args) {
			 //启动工厂
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
				//获取对象
        UserDAO userDAO = (UserDAO) context.getBean("userDAO");
    }

4. Spring 框架的核心思想

4.1 IOC [控制反转]

IOC定义:将对象的创建由原来(new)的方式转移到配置文件中,交给spring工厂来创建对象

DI定义:又叫依赖注入。Spring不仅要创建对象,还要建立类与类之间的关系,即对象与对象之间的关系,比方说controller层有service层对象,因此在控制反转的基础上又提出了依赖注入的概念。

DI语法:
    1:组件中需要谁就将谁声明为成员变量并提供公开的SET方法。 
    	比如说在controller中定义 private UserService userService; 同时提供userService set方法
	2: 在Spring 配置文件中对应的组件内部使用 property 标签完成赋值操作。即在controller bean中 注入			service。
        注意:注入对象时使用 赋值使用 ref   基本数据类型或string或日期,使用 value标签
        	 list 使用 list set 使用set map使用map  properties 使用 props

4.1.1 DI注入方式

​ 有三种注入方式:

  • SET注入-->就是上面的写法,最主要

  • 构造注入

    	<constructor-arg index="0" name="id" value="1"/>
    	<constructor-arg index="1" name="name" value="xiaohei"/>
    	<constructor-arg index="2" name="age" value="12"/>
    	<constructor-arg index="3" name="qqs">
        <array>
          <value>xxx</value>
          <value>222</value>
          <value>333</value>
        </array>
    	</constructor-arg>
    

    注意:构造注入并不常用,不过在一些框架类中必须使用构造注入,这里先了解其注入语法即可。

  • 自动注入

    autowire=”byName”

根据注入的属性名与配置文件中bean的id匹配,一致则注入,不一致报错

autowire=”byType”

根据注入的属性类型,与配置文件中的类型匹配,类型一致注入(在多个实现类时,会产生歧义)

注意: 无论使用以上那种方式注入都需要为属性提供set方法

5.bean相关

5.1 bean的创建模式

singleton:单例 默认

​ 在工厂中全局唯一,只创建一次

prototype: 多例

全局不唯一,每次使用都会创建一个新的对象

5.2 bean的生产原理

原理: 反射+构造方法

 UserDAOImpl userDAO = (UserDAOImpl) 	   
 Class.forName("com.baizhi.dao.UserDAOImpl").newInstance();
 System.out.println(userDAO);

5.3 bean的生命周期

  • 何时创建

    随着工厂启动, 所有单例bean随之创建 非单例的bean,每次使用时创建

  • 何时销毁

    工厂关闭,所有bean随之销毁 ( 注意: spring对多例bean管理松散,不会负责多例bean的销毁)

5.4 bean工厂创建对象的好处

  1. 使用配置文件管理java类,再生产环境中更换类的实现时不需要重新部署,修改文件即可
  2. spring默认使用单例的模式创建bean,减少内存的占用
  3. 通过依赖注入建立了类与类之间的关系(使java之间关系更为清晰,方便了维护与管理)

6.代理

6.1 什么是代理

代理: 指的是java中的一种设计模式

6.2 为什么需要代理

很多时候除了当前类能够提供的功能外,我们还需要补充一些额外功能。

6.3 代理的作用

​ 代理对象可以在客户和目标对象之间起到中介作用,从而为目标对象增添额外的功能

6.4 开发代理的原则

开发代理的原则: 代理类和目标类功能一致且实现相同的接口,同时代理类中依赖于目标类对象

6.5 动态代理原理

public class TestDynamicProxy {
     public static void main(String[] args) {
   		//使用动态代理对象:  指的是在程序运行过程中动态通过代码的方式为指定的类生成动态代理对象    
		UserService userService = new UserServiceImpl();//目标类
  	  //proxy 用来生成动态对象的类

    //参数1: classLoader 类加载器
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

    //参数2: Class[] 目标对象的接口的类型的数组
    Class[] classes = {UserService.class};

    //参数3: InvocationHandler接口类型  invoke 方法 用来书写额外功能 附加操作

    //dao  Userdao    userdao = sqlSession.getMapper(userdao.class);  userdao.save() =

    //返回值: 创建好的动态代理对象
    UserService proxy = (UserService) Proxy.newProxyInstance(classLoader, classes, new InvocationHandler() {
        @Override
        //通过动态代理对象调用自己里面代理方法时会优先指定invokcationHandler类中invoke
        //参数1: 当前创建好的代理对象  参数2:当前代理对象执行的方法  参数3:当前代理对象执行方法的参数
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

            System.out.println("当前执行的方法: "+method.getName());
            System.out.println("当前执行的方法的参数: "+args[0]);

            try {
                System.out.println("开启事务");
                //调用目标类中业务方法通过反射机制 调用目标类中当前方法
                Object invoke = method.invoke(new UserServiceImpl(), args);
                System.out.println("提交事务");
                return invoke;
            }catch (Exception e){
                e.printStackTrace();
                System.out.println("回滚事务");
            }
            return null;
        }

    });

    System.out.println(proxy.getClass());
    String result = proxy.findAll("小陈");//通过动态代理对象调用代理中方法
    System.out.println(result);
}
}

6.6 AOP编程

  `通知(Advice)`: `除了目标方法以外的操作都称之为通知`
 	`切入点(PointCut): 要为哪些类中的哪些方法加入通知`
 	`切面(Aspect)`: `通知 + 切入点`

6.6.1 编程

6.6.1.1 前置通知
# 1.引入依赖
	 spring-aop
	 spring-expression
	 spring-aspects

# 2.开发通知类
	  MethodBeforeAdvice      前置通知
	  MethodInterceptor       环绕通知
	  AfterReturningAdvice    返回后通知
	  ThrowsAdvice						异常通知
	  
	  MyAdvice implements  通知接口{.....}
	  
    //自定义通知类:用来完成额外功能
    public class MyAdvice  implements MethodBeforeAdvice {
        @Override//参数1:当前调用的方法对象    //参数2:当前调用方法对象的参数  //参数3:目标对象
        public void before(Method method, Object[] objects, Object o) throws Throwable {
            System.out.println("目标方法名: "+method.getName());
            System.out.println("目标方法的参数: "+objects);
            System.out.println("目标对象: "+o.getClass());
        }
    }
# 3.配置切面
		a.引入aop命名空间
 			 <?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.xsd 
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

		b.管理通知
			 <!--管理通知类 通知类bean配置-->
    	 <bean id="myAdvice" class="before.MyAdvice"/>
             
   		c.配置切面
   		<aop:config>
            //配置切入点, 这个id用来下方  pointcut-ref 上。  execution(* 包.类.方法(返回值,不关心就用..代替))
        <aop:pointcut id="pc" expression="execution(* before.UserServiceImpl.*(..))"/>
         //advice-ref 后面跟的是 代理类的bean id 即管理通知类bean   pointcut-ref 后面跟的是往哪切,切那个类下的或者哪个包下的
        <aop:advisor advice-ref="myAdvice" pointcut-ref="pc"/>
    	</aop:config>

# 4.启动工厂测试 main函数调用
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        UserService userSerivce = (UserService) context.getBean("userService");
        System.out.println(userSerivce.getClass());
        userSerivce.save("小黑");
6.6.1.2 环绕通知
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

import java.util.Date;
//自定义环绕通知用来记录目标方法的执行时长
public class MethodInvokeTimeAdvice implements MethodInterceptor {

    //参数1: invocation 获取当前执行方法 获取当前执行方法参数 获取目标对象
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("==========进入环绕通知===========");
//        System.out.println("当前执行方法:" +invocation.getMethod().getName());
//        System.out.println("方法的参数: "+invocation.getArguments()[0]);
//        System.out.println("获取当前的目标对象: "+invocation.getThis());
        try{
            long start = new Date().getTime();
            //放行目标方法
            Object proceed = invocation.proceed();//继续处理----这一步是执行目标方法
            long end = new Date().getTime();
            System.out.println("方法: "+invocation.getMethod().getName()+",本次执行了 ["+(end-start)+"] ms!");
            return proceed;
        }catch (Exception e){
            e.printStackTrace();
            System.out.println("出现异常时业务处理");
        }
        return null;
    }
}
6.6.1.3 后置通知、异常通知
//只用后置通知时  实现 AfterReturningAdvice 这个   实现这个方法 afterReturning
//自定义后置通知和异常通知
public class MyAfterAdvice  implements AfterReturningAdvice, ThrowsAdvice {

    //后置通知
    //参数1:目标方法返回值  参数2: 当前执行方法对象  参数3:执行方法参数  参数4:目标对象
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("==========进入后置通知=========");
        System.out.println("返回值: "+returnValue);
        System.out.println("方法名: "+method.getName());
        System.out.println("方法的参数: " + args[0]);
        System.out.println("目标对象: " +target);
    }


    //出现异常时执行通知处理
    public void afterThrowing(Method method, Object[] args, Object target, Exception ex){
        System.out.println("===========进入异常通知============");
        System.out.println("方法名: "+method.getName());
        System.out.println("方法的参数: " + args[0]);
        System.out.println("目标对象: " +target);
        System.out.println("异常信息: "+ex.getMessage());
    }


}

6.7 表达式

6.7.1.execution 方法级别的切入点表达式

    expression="切入点表达式"

    1.execution 切入点表达式   --->方法级别的切入点表达式  控制粒度: 方法级别   效率低

        完整语法:
                1.execution(访问权限修饰符 返回值 包名.类名.方法名(参数类型))
                2.execution(返回值 包名.类名.方法名(参数类型))
                  * 任意多个字符


        1).execution(* com.baizhi.service.*.*(..))              [比较多一种]
            包:      com.baizhi.service
            类:      任意类
            方法:     任意方法
            参数:     参数任意
            返回值:   任意返回值类型

        2).execution(String com.baizhi.service.*ServiceImpl.*(..))
            包:      com.baizhi.service
            类:      以ServiceImpl结尾的类
            方法:     方法名任意
            参数:     任意参数
            返回值:    返回值必须String类型相关方法

        3).execution(String com.baizhi.service.*Service*.*(String))
            包:      com.baizhi.service
            类:      类名中包含Service关键字的类
            方法:    任意
            参数:    参数只有一个类型必须是String
            返回值:  返回值必须是String

        4).execution(* com.baizhi.service..*.*(..))             [比较多一种]
            包:     com.baizhi.service及这个包中子包的子包
            类:     任意类
            方法:   任意方法
            参数:   任意参数
            返回值:  任意类型

        5).execution(* com.baizhi.service.*ServiceImpl.*(..)) [比较多一种]
            包:     com.baizhi.service 包
            类:     以ServiceImpl结尾的类
            方法:   任意方法
            参数:   任意参数
            返回值:  任意类型

        6).execution(* *.*(..))   //全部方法 避免使用这种方式
            包: 项目中所有包
            类: 项目中所有类
            方法: 所有方法
            参数: 所有参数
            返回值: 任意类型

            注意: 尽可能精准切入 避免不必要的切入

6.7.2.within 类级别的切入点表达式

# 1.语法
	within(包.类)

# 2.示例
	within(com.baizhi.service.*ServiceImpl) 
			包: com.baizhi.service
			类: 所有类中所有方法不关心返回值和参数
	
	within(com.baizhi.service.UserServiceImpl)
			包: com.baizhi.service
			类: UserServiceImpl类中所有方法不关心返回值和参数

注意:within的效率高于execution表达式,推荐使用within表达式

7 注解

7.1 实例化相关注解

# 1. @Component(value="beanid")
				修饰范围:    用在类上
				注解作用:    通用的创建实例的注解,用来创建当前这个类的实例
				value属性:	用来指定创建的对象在工厂中的唯一标识   如果不指定默认创建对象在工厂中的标识为类名首字母小写
				
# 2. @Repository 
				修饰范围:    用在类上
				注解作用:    @component的子类注解专用于DAO组件的创建,通常加在DAO组件上
				value属性:	用来指定创建的对象在工厂中的唯一标识   如果不指定默认创建对象在工厂中的标识为类名首字母小写

# 3. @Service
				修饰范围:    用在类上
				注解作用:    @component的子类注解专用于Service组件的创建,通常加在Service组件上
				value属性:	用来指定创建的对象在工厂中的唯一标识   如果不指定默认创建对象在工厂中的标识为类名首字母小写

# 4. @Controller
			 	修饰范围:    用在类上
				注解作用:    @component的子类注解专用于Action组件的创建,通常加在Action组件上
				value属性:	用来指定创建的对象在工厂中的唯一标识   如果不指定默认创建对象在工厂中的标识为类名首字母小写

7.2 控制对象的创建次数相关注解

 # 1. @Scope(value="singleton|prototype")
 			  修饰范围:    用在类上
			  注解作用:    用来控制这个实例在工厂中的创建次数
			  value属性:	singleton为单例,prototype为多例   默认单例

7.3 注入相关的注解

# 1. @Autowired(Spring提供)
				修饰范围:    用在成员变量或成员变量的GET/SET方法上
				注解作用:		 用来给类中成员变量赋值
				注入原则:    默认根据类型自动注入

# 2. @Resource(JAVAEE提供)
				修饰范围:    用在成员变量或成员变量的GET/SET方法上
				注解作用:		 用来给类中成员变量赋值
				注入原则:    默认根据名称自动注入名称找不到根据类型自动注入

7.4 控制事务的相关注解

# 1. @Transactional
			  修饰范围:    用在类上主要用在业务层组件类上或者是方法上
				注解作用:		 用来给类中方法加入事务,当类上和方法上同时存在该注解时局部优先
				注解属性:    
							propagation  用来控制传播属性
							Isolation    用来控制隔离级别
							timeout      用来设置超时性
							rollback-for 用来设置什么异常回滚
							norollback-for 用来设置什么异常不会滚
							readonly     用来设置事务读写性
posted @ 2021-06-20 19:54  neoQVQ  阅读(35)  评论(0)    收藏  举报