Day 44 学习分享 - Spring

Spring的核心功能
IOC:
	方便解耦, 简化开发
	IOC不是一种技术而是一种设计思想

AOP:
	面向切面编程
	Spring提供面向切面编程, 可以方便的实现对程序进行权限拦截、运行监控等功能
	
声明式事务的支持:
	通过配置完成对事务的管理, 无需手动编程
	
方便的测试功能:
	Spirng-test模块可以继承Junit依赖, 通过注解进行测试
	
框架整合:
	Spring框架可以无缝整合其他框架
Spring的核心模块
spring-core: 依赖注入IoC与DI的最基本实现
spring-beans: Bean工厂与bean的装配
spring-context: spring的context上下文即IoC容器
spring-context-support
spring-expression: spring表达式语言
什么是IOC
IOC - Inversion of Control 控制反转思想

	传统的创建对象方法是通过new 关键字, 而spring则是通过IoC容器来创建对象
	也就是说我们将对象的创建、初始化、销毁等工作交给了IoC容器! 以达到程序的高度解耦的目的!
	Spring容器可以说是一个IoC容器(装对象, 对象的容器!)
DI - 依赖注入
DI - Dependency Injection
	
	在创建对象实例时, 同时为这个对象注入它所依赖的属性
	相当于把bean与bean之间的关系交给容器处理, 而这个容器就是spring
applicationContext.xml配置文件详解
根标签:
	beans

子标签:
	bean
	如果要注入对象依赖, 则是根标签内必须包含的标签, 用于声明具体的类的对象

bean标签对应属性:
	class --> 指定bean对应类的全路径
	name --> name是bean对应对象的一个标识
	scope --> bean对象创建模式和生命周期
	id --> bean对象唯一标识, 不能添加特别字符!
	lazy-init --> 是否延时加载, 默认值为false
	init-method --> 对象初始化方法
	destroy --> 对象销毁方法

Scope属性:
	singleton - 默认值, 容器创建的对象是单例
	prototype - 创建的对象是多例(每次使用都创建新对象)
	request - 一次请求范围内, 容器中的对象保持同一个
	session - 一次会话范围内, 容器中的对象保持同一个
	global session - 类似HTTP session的作用域, 但是仅仅在基于protlet的web应用中才有意义

lazy-init属性在使用时需注意, 只有在单例模式下才有意义
Spring框架创建对象方式
1. 调用的默认的无参数的构造方法创建对象
<bean id = "s1" class = "com.spring.bean.Student">
	<property name = "id" value = "9527"/>
    <property name = "name" value = "小张"/>
    <property name = "money" value = "123.45"/>
    <property name = "age" value = "18"/>
</bean>

2. 调用有参数构造方法创建对象
<bean id = "s2" class = "com.spring.bean.Student">
	<constructor-arg name = "name" value = "周星星" index = "1" type = "java.lang.String"/>
	<constructor-arg name = "id" value = "1110" index = "0" type = "java.lang.Integer"/>
	<constructor-arg name = "age" value = "45" index = "2" type = "java.lang.Integer"/>
	<constructor-arg name = "money" value = "456.78" index = "3" type = "java.lang.Double"/>
</bean>
 
3. 调用静态工厂创建对象
	<bean id = "s3" class = "com.spring.bean.Student" factory-method = "createStudent"/>
	
4. 调用非静态工厂创建对象
	<bean id = "factory1" class = "com.spring.bean.StudentFactory1"/>
	<bean id = "s4" factory-bean="factory1" factory-method="createStudent"/>
依赖注入
1. Set方法注入

1.1 基本类型注入
	<bean name = "person" class = "com.itqf.spring.bean.Person">
		<property name = "name" value = "jeck"/>
        <property name = "age" value = "11"/>
	</bean>

1.2 引入类型注入
	<bean name = "person" class = "com.itqf.spring.bean.Person">
        <property name = "name" value = "helen"/>
        <property name = "age" value = "18"/>
        <property name = "car" ref = "car"/>
	</bean>
	
	<bean name = "car" class = "com.itqf.spring.bean.Car">
		<property name = "name" value = "MINI"/>
        <property name = "color" value = "grey"/>
	</bean>

2. 构造方法注入
2.1 单个有参数构造方法注入
	<bean name = "person" class = "com.itqf.spring.bean.Person">
    	<constructor-arg name = "name" value = "rose"/>
        <constructor-arg name = "age" value = "18"/>
    <bean/>
        
2.2 多个构造方法但参数种类顺序不同时用Index定位
    <bean name = "person" class = "com.itqf.spring.bean.Person">
    	<constructor-arg name = "name" value = "helen" index = "0"/>
        <constructor-arg name = "age" value = "18" index = "1"/>
    </bean>
        
2.3 多个构造方法参数名和位置一致但种类不同时用type定位
    <bean name = "person" class = "com.itqf.spring.bean.Person">
    	<constructor-arg name = "name" value = "9527" type = "java.lang.Integer"/>
        <constructor-arg name = "age" value = "18" type = "java.lang.Integer"/>
    </bean>
        
3. P名称空间注入
    此方法需要修改xml文件表头
        xmlsn:p="http://www.springframework.org/schema/p"
    <bean name = "person1" class = "com.iqtf.srping.bean.Person" p:name = "jack" p:age = "18"/>

4. spel注入
    <bean name = "person" class = "com.iqtf.srping.bean.Person" p:name = "#{person1.name}" p:age = "#{person1.age}"/> (调用了上方的数据)
注解
Spring注解开发:注解简化开发!
   
	@Component:给类生成对象,类注解,对象名默认是类名(首字母小写),也可以自己定义名字!例如:@Component("p1")

	@Value:给属性赋值 属性或者set方法注解   @Value("${p.pass}") 引用外部的属性文件

	@AutoWired:根据类型自动装配对象。如果匹配到多个对象,直接异常!

		@Autowired //根据类型自动装配, 万一Spring框架中有多个对象可以匹配?
		@Qualifier("a1") //指定使用容器中的哪一个对象!

	@Resource:如果没有配置名字,那么就根据类型匹配,如果匹配到多个类型,那么异常。如果指定了名字,那么根据名字匹配!

		@Resource("name") == @Autowired  + @Qualifier("name")

		@Resource(name = "${dog.type}")

	@PostConstruct和 @PreDestroy 注解,对象初始化和销毁的注解方法

	@Scope("prototype") 设置类单例还是多例
	@Scope("singleton") 默认单例


	@Repository Dao层 数据库访问层创建对象注解

	@Service Service层 业务逻辑层创建对象注解

	@Controller Servlet层 表述层创建对象注解
   

	@RunWith(SpringJUnit4ClassRunner.class)
	@ContextConfiguration("classpath:applicationContext.xml") JUnit和test核心组合使用注解!
AOP静态/动态代理
AOP - Aspect Oriented Programming 面向切面编程
OOP - Object Oriented Programming 面向对象编程

个人对AOP理解:
	在不修改源代码的情况下, 对源代码进行功能的添加, 例如日志功能、事务功能....
	
案例需求:
	有一个User类下存在添加用户和删除用户两个方法, 需要对两个方法添加开启事务和提交事务的功能.
	1. 静态代理:
		创建一个代理类, 继承Service接口(继承与核心源代码相同的接口), 在代理类中重写添加和删除方法, 在其中添加开启和提交事务的功能
		
	2. JDK动态代理:
		通过继承InvocationHandler接口来完成JDK动态代理
		public class ObjectInterceptor implements InvocationHandler {
            // 目标类
            private Object target;
        
        	// 切面类(这里是指事务类)
        	private Mytransaction transaction;
            
            // 构造方法赋值
            public ObjectInterceptor (Object target, Mytransaction transaction) {
                this.target = target;
                this.transaction = transaction;
            }
            
            // InvocationHandler接口内方法
            public Object invoke (Object proxy, Method method, Object[] args) throws Throwable {
                // Mytransaction类内开启事务方法
                this.transaction.before();
                // 调用目标类方法
                method.invoke(this.target, args);
            	// Mytransaction类内提交事务方法
            	this.transaction.after();
                return null;
            }
        }

		public class Test {
            @Test
            pubic void test()) {
                Object target = new UserServiceImpl();
                
                // 拦截器
                ObjectInterceptor proxy = new ObjectINterceptor(target, new Mytransaction());
                
                // Proxy接口方法获得代理类对象
                // 三个参数的含义: 1. 目标类的类加载器 2.目标类所有实现的接口 3.拦截器
                UserService userService = (UserService) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), proxy);
                userService.addUser(new User(....));
                userService.deleteUser(1);
            }
        }
	通过反编译Spring框架自动生成的接口实现类的.java文件可以发现, 底层代码其实是调用了父类的invoke方法.
        
    可以从JDK代理的核心方法invoke看出他只能为接口创建代理实例, 而当我们需要创建的代理实例是一个类时, 我们就需要使用CGLib代理替代他.
        
    3. CGLib代理 - 字节码技术, 可以为类创建子类, 在子类中拦截所有父类的方法并植入逻辑
        
        需要导入依赖
        <dependency>
        	<groupId>cglib</groupId>
        	<artifactId>cglib</artifactId>
        	<version>2.2</version>
        </dependency>
	
	public class CGLibProxy implements MethodInterceptor {
        // 子类生成器
        private Enhancer enhancer = new Enhancer();
    
    	private MyTransaction transaction;
        
        public CGLibProxy (MyTransaction transaction) {
            this.trsansaction = transaction;
        }
    
        // 设置被代理对象
        public Object getProxy(Class clazz) {
            enhancer.setSuperclass(clazz);
            enhancer.setCallback(this);
            return enhancer.create();
        }
        
        public Object intercept(Object obj, Method method, Object[] ojbs, MethodProxy methodProxy) throws Throwable {
            transaction.before();
            Object invoke = methodProxy.invokeSuper(obj, ojbs);
            transaction.after();
            return invoke;
        }
    }

	@Test
	public void test() {
        CGLibProxy proxy = new CGLibProxy(new MyTransaction());
        UserServiceImpl userService =(UserServiceImpl)  proxy.getProxy(UserServiceImpl.class);
    
    	userService.addUser(new User(...));
        userService.deleteUser(1);
    }		
		
JDK和CGLib动态代理的区别:
	1. JDK动态代理生成的代理类和委托类实现了相同的接口
    2. CGLib动态代理中生成的代理类是委托类的子类, 且不能处理被Final修饰的方法
    3. JDK采用反射机制调用委托类的方法, CGLib采用类似索引的方式直接调用委托类方法
		
AOP注解编程 *
术语:
	1. target: 目标类, 需要被代理的类
    2. joinpoint: 连接点, 指可能被拦截到的方法
    3. pointCut: 切入点, 已经被增强的连接点
    4. advice: 通知/增强, 增强代码
    5. weaving: 织入, 指把advice应用到目标对象target来创建新的代理对象proxy的过程
    6. proxy: 代理类, 通知 + 切入形成的子类或实体类
    7. aspect: 切面, advice和pointcut的结合
           
    增强类环绕通知参数:ProceedingJoinPoint       
Xml配置文件格式:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
	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.2.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.2.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
       
	<bean name="userService" class="com.bruceliu.service.UserServiceImpl" />
	
	<!--配置通知对象 -->
	<bean name="myAdvice" class="com.bruceliu.dao.MyAdivce" />
	
	<!-- 配置将增强织入目标对象 -->
	<aop:config>
		  <!--
             com.itqf.spring.service.UserServiceImpl
             1 2 3 4
             1: 修饰符  public/private/*  可忽略
             2: 返回值  String/../*
             3: 全限定类名  类名  ..代表不限层数  *ServiceImpl
             4: (..)
            -->
		<aop:pointcut id="pc" expression="execution(* com.bruceliu.service.*ServiceImpl.*(..))" />
		<aop:aspect ref="myAdvice">
			<aop:before method="before" pointcut-ref="pc" />
			<aop:after-returning method="afterReturning" pointcut-ref="pc"/>
			<aop:around method="around" pointcut-ref="pc" />
			<aop:after-throwing method="afterException" pointcut-ref="pc" />
			<aop:after method="after" pointcut-ref="pc" />
		</aop:aspect>
	</aop:config>
</beans>
注解格式:
	<!-- 配置将增强织入目标对象 使用注解的方式 -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
	<!-- 扫描类 -->
	<context:component-scan base-package="com.aop.*">
package com.aop.advice;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class MyAdvice {

    @Pointcut("execution(* com.aop.service.impl.*.*(..))")
    public void pc() {}

    @Around("MyAdvice.pc()")
    public Object aroundMethod(ProceedingJoinPoint pjp) {
        Object o = null;

        try {
            System.out.println("前置通知");
            o = pjp.proceed();
            System.out.println("后置通知");
        } catch (Throwable throwable) {
            System.out.println("出现异常, 信息为:" + throwable.getMessage());
        } finally {
            System.out.println("最终通知");
        }
        return o;
    }
}
Spring整合Mybatis
依赖导入:

	<!--德鲁伊数据库连接池-->
	<dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>${druid.version}</version>
   </dependency>

	<!--Mybatis和Spring整合包-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>${mybatis.spring.version}</version>
    </dependency>


	<!--如果Mapper.xml文件没有放在resource下而是放在了java文件夹中, 带上这句配置以避免xml文件不编译-->
    <resources>
      <resource>
        <directory>src/main/java</directory>
        <includes>
          <include>**/*.xml</include>
        </includes>
      </resource>
	</resources>

JDBC.properties:
	jdbc.Driver=com.mysql.jdbc.Driver
    jdbc.Url=jdbc:mysql://127.0.0.1:3306/ssm20190917?characterEncoding=utf-8
    jdbc.username=root
    jdbc.Password=123456 
applicationContext.xml配置:
	<!--1. 扫描程序中的包 -->
	<context:component-scan base-package="com.sm.*">
        
    <!--2. 加载属性文件-->
    <context:property-placeholder location="classpath:jdbc.properties">
        
    <!--3. 配置德鲁伊数据源-->
    <bean id="ds" calss="com.alibaba.druid.pool.DruidDataSource">
    <!-- 此处也可以使用JDK自带的数据池 PooledDataSource -->
    <!-- 但是要注意其中的数据库名属性是driver -->
    	<property name="driverClassName" value="${jdbc.Driver}"/>
        <property name="url" value="${jdbc.Url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.Password}"/>
    </bean>
    
    <!-- 4. 配置Mybatis的SqlSessionFactory -->
    <bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 需要记住类名是SqlSessionFactoryBean -->
        <!-- 配置数据库连接池 -->
    	<property name="dataSource" ref="ds"/>
        <!-- 配置Mybatis配置文件的位置 但是一般配置文件一般为空 所以可以省略这一步 -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <!-- 配置Mapper.xml的位置 注意value中文件以'/'分割 -->
        <property name="mapperLocations" value="classpath:com/sm/mapper/*.xml"/>
    	<!-- 配置实体类别名 -->
        <property name="typeAliasesPackage" value="com.sm.bean"/>    
	</bean> 
        
        
    <!-- 5. Mapper接口层使用Spring自动生成实现类对象 -->
	<bean>
        <!-- 记住name='MapperScannerConfigurer' -->
        <property name="org.mybatis.spring.mapper.MapperScannerConfigurer" value = "com.sm.mapper"/>
	</bean>
        
    <!-- 6. 配置平台事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="ds"/>
	</bean>
        
    <!-- 7. 开启事务注解配置 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>    
    默认情况下<tx:annotaion-driven/> 中transaction-manager属性会自动使用名为"transactionManager"的事务管理器
    所以, 如果用户将事务管理器(第六步)中的id定义为"transactionManager"可以将其简化为<tx:annotaion-driven/>
Spring事务基本属性
@Transactional注解后方()中可以配置

只读:read-only(默认值为false)
	设置事务是否对后端的数据库进行修改操作
	可以在查询方法中设置只读属性为true以提高性能

事务超时:time-out(时间单位为秒 默认值为-1)
	当事务运行太长的时间, 自动回滚, 而不是一直等待其结束
	
回滚规则: 默认遇到运行时异常事务回滚
	no-rollback-for()
	rollback-for()

隔离级别: 四大隔离级别
	默认的隔离级别和数据库相同, 例如Mysql默认的隔离级别是repeatable-read 
	而Oracle的默认隔离级别是Read-Commited

传播行为: 七大传播行为 
	Propagation_REQUIRED:
		默认值, 如果当前没有事务, 就新建一个事务
		如果已经存在一个事务中就加入到这个事务中
	Propagation_SUPPORTS:
		支持当前事务, 如果当前没有事务, 就以非事务方式执行
	Propagation_MADATORY:
		使用当前事务, 如果没有事务就抛出异常
	Propagation_REQUIRES_NEW:
		新建事务, 如果当前存在事务就把当前事务挂起
	Propagation_NOT_SUPPORTED:
		以非事务方式执行操作, 如果当前存在事务就把当前事务挂起
	Propagation_NEVER:
		以非事务方式执行操作, 如果当前存在事务则抛出异常
	Propagation_NESTED:
		如果当前存在事务, 则在嵌套事务内执行
		如果当前没有事务则执行与REQUIRED类似的操作
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值