Spring总结

x33g5p2x  于9个月前 转载在 Spring  
字(15.4k)|赞(0)|评价(0)|浏览(74)

前言

提示:以下是本篇文章正文内容,下面案例可供参考

一、Spring

官方

二、Spring核心IOC控制反转

Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。
IoC 是指在程序开发中,实例的创建不再由调用者管理,而是由 Spring 容器创建。Spring 容器会负责控
制程序之间的关系,而不是由程序代码直接控制,因此,控制权由程序代码转移到了 Spring 容器中,控
制权发生了反转,这就是 Spring 的 IoC 思想。
	简单来说就是把 new对象的权利 让Spring框架进行接管,不需要我们new对象

2.1 Spring入门案例

1.创建Maven项目
	2.导入Spring的pom依赖
		 <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.9</version>
        </dependency>
	3.在resources目录下创建 applicationContext.xml/application.xml 文件夹
	4.在beans里面创建 bean对象 让spring接管你的实体类bean
	5.测试:
		5.1 创建BeanFactory beanFactory=new XmlBeanFactory(new FileSystemResource("Spring配置文件名称")); 			  beanFactory.getBean(“bean的Id”)
		不推荐使用!  BeanFactory是ApplicationContext 的一个父接口
		5.2 创建 ApplicationContext applicationContext=new ClassPathXmlApplicationContext(“application.xml”);来找到容器
		Team team = (Team) applicationContext.getBean("team"); //从容器中通过ID获取对象
		推荐使用!
		5.2测试

测试结果如下:
Spring会通过反射来获取我们的对象

2.2 bean标签的属性

1. class 指定bean对应类的全路径
	2. name name是bean对应对象的一个标识
	3. scope 执行bean对象创建模式和生命周期,scope="singleton"和scope="prototype"
		singleton: 单例 默认值 当我们的Spring容器被创建时,带有单例属性的bean对象就被创建了,而且容器中只有唯一的一个对象
		prototype: 原型(多例) 多例对象什么时候使用什么时候创建,每次使用都会创建一个新的对象
	4. lazy-init true/false  :懒加载 
		true:真懒 获取对象的时候才会创建对象
		false:不懒  默认值 立即创建对象 不管使用不使用
	5. init-method 只需要加载配置文件即可对象初始化方法 方法必须在实体类bean中创建了
	6. destroy-method 对象销毁方法 方法必须在实体类bean中创建了,销毁时需要调用 容器.close()方法来完成销毁。

2.3 创建对象的三种方法

1. 通过配置文件中创建bean创建会调用无参数构造方法
		<bean id="team" class="com.mk.study.Team"></bean>
	2. 通过配置文件中创建bean后在bean标签中写入
		<bean id="team2" class="com.mk.study.Team" scope="prototype">
	        <constructor-arg name="id" value="111"/>
	        <constructor-arg name="name" value="张三" />
	        <constructor-arg name="location" value="xxx"/>
    	</bean>
	3. 工厂方法
		public class MyFactory {
    	public Team t(){
        	System.out.println("MyFactory --- 实例方法");
       	 	return new Team(1111,"1111","1111");
    	}
   		public static Team t2(){
        	System.out.println("MyFactory --- 静态方法");
        	return new Team(2222,"2222","2222");
    	}
	}
	<!--    静态方法    可以直接通过bean的工厂方法直接调用-->
    <bean id="myFactory" class="com.mk.study.MyFactory" factory-method="t2"></bean>
	<!--    实例方法    通过factory-bean拿到对象 在调用factory-method实例方法    -->
    <bean id="factory" class="com.mk.study.MyFactory"></bean>
    <bean id="instanceTeam" factory-bean="factory" factory-method="t"></bean>

2.4 基于XML的DI(Dependency injection)依赖注入

1. 通过set注入 (常用)
			 <bean class="com.mk.study.dao.TeamDao" id="teamDao"></bean>
			    <bean id="service" class="com.mk.study.service.TeamService">
			    <!--        通过set方法注入实例对象,对象中必须有setter方法,否则无法注入实例对象 -->
			    <property name="teamDao" ref="teamDao"></property>
			 </bean>
	2. 通过构造方法来完成注入
		 <bean class="com.mk.study.dao.TeamDao" id="teamDao"></bean>
		<bean id="service2" class="com.mk.study.service.TeamService">
	        <!--        通过使用有参数构造方法来完成注入 实例对象   -->
	        <constructor-arg name="teamDao" ref="teamDao"></constructor-arg>
    	</bean>
	3. 自动注入
		<bean class="com.mk.study.dao.TeamDao" id="teamDao"></bean>
		通过匹配service3中的引用属性和bean注入的对象 类型 匹配上 就会自动注入到service3中
		<bean id="service3" class="com.mk.study.service.TeamService" autowire="byType/byName"></bean>

2.5 基于注解实现IOC

1. @Component(value="自定义id名") 等于把当前对象交给Spring容器进行管理
	2. @Component 若不写value值 默认是对象名  首字母小写后面不变
    @Component("teamDao") == <bean class="com.mk.study.dao.TeamDao" id="teamDao"></bean>
    我们在对象上引入了 @Component()后需要在Spring的XML配置文件中扫描注解标签:
    <context:component-scan base-package="扫描的包名"/>
	Component的相关注解:
		1. @Repository 加在Dao实现类上
		2. @Service 加在Service实现类上
		3. @Controller 加在Controller实现类上

2.6 基于注解实现属性注入

1. @Value("属性值") 可以放在实体类的属性中,来给属性赋值
		注意: 当@Component()交给Spring接管后,刚刚创建对象时,@Value("属性值")是没有生效的,当Spring创建完毕对象后才给对象的属性赋值。
		@Value() 是通过setter方法来给属性赋值,setter方法可以省略
	2. @Autowired 添加在对象的引用类型(对象属性)中,当我们给引用属性的对象添加了@Component或依赖注入后,我们的Spring容器会管理对象之间的依赖关系,当写入@Autowired给引用对象属性中后,Spring容器启动后会查找相同的对象类型自动装配给@Autowired下的引用类型属性。
	@Autowired
    public TeamDao teamDao;
    3. @Autowired与@Qualifier("指定依赖注入的id名") 当我们依赖注入相同的对象时,我们想指定某一个对象,可以在@Qualifier("指定依赖注入的id名")中写入指定对象的id标识

三、Spring核心AOP面向切面编程

3.1 什么是AOP

AOP为Aspect Oriented Programming的缩写,意思为面向切面编程,是通过预编译方式和运行期动态
代理实现程序功能的统一维护的一种技术。
AOP的作用:不修改源码的情况下,程序运行期间对方法进行功能增强
好处:1、减少代码的重复,提高开发效率,便于维护。
 2、专注核心业务的开发。
核心业务和服务性代码混合在一起
开发中:各自做自己擅长的事情,运行的时候将服务性代码织入到核心业务中。
通过spring工厂自动实现将服务性代码以切面的方式加入到核心业务代码中。

3.2 AOP的实现机制-动态代理

1. 什么是代理模式?
		代理:自己不做,找人帮你做。
		代理模式:在一个原有功能的基础上添加新的功能。
		分类:静态代理和动态代理

3.2.1 静态代理

1. 基于类的静态代理
		要求继承被代理的类
		缺点: 每次只能代理一个类

	public class StaticProxy extends TeamService {
    public void add(){
        try {
            System.out.println("开启事务");
            super.add();//核心业务由被代理的对象来完成,其他服务性功能由代理来完成
            System.out.println("提交事务");
        }catch (Exception e){
            System.out.println("事务回滚");
        }

    }
}

基于接口的静态代理

3.2.2 基于Jdk的动态代理

1. 实现接口 InvocationHandler 实现 invoke()方法
	2. 实现 
	被代理对象的接口 name=(被代理对象的接口)Proxy.newProxyInstance("拿到被代理对象的类加载器", "拿到被代理对象的接口", new InvocationHandler() {
            /**
             * 该方法在目标类的方法被执行时,会被调用,你在调用被代理类的方法时,会先执行invoke(...)这个方法
             * @param proxy 代理对象
             * @param method 代理对象的方法
             * @param args 代理对象的参数
             * @return
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            	//此代码是真正执行的目标方法
                Object result = method.invoke("被代理的对象", "被代理对象执行方法需要的参数");
                //被代理对象的方法执行完毕后的返回值,没有返回null
                return result;
            }
        })
        name.方法();

3.2.3 CGLIB动态代理

1. CGLIB动态代理使用于 代理对象没有接口的情况
	2.
	//目标对象 没有接口
        NBAService service=new NBAService();
        NBAService service1= (NBAService) Enhancer.create(service.getClass(), new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("cglib动态代理");
                //核心业务代码
                Object result = methodProxy.invokeSuper(o, objects);
                System.out.println("cglib动态代理");
                return result;
            }
        });
        service1.add("张三",1);

3.3 AOP相关概念

1. Target(目标对象)  要被增强的对象,一般是业务逻辑类的对象。
	2. Proxy(代理)一个类被 AOP 织入增强后,就产生一个结果代理类。
	3. Joinpoint(连接点) 类里面哪些方法可以被增强,这些方法称为 连接点
	4. Pointcut(切入点)  实际被真正增强的方法,称为切入点
		切入点表达式:execution(访问权限 方法返回值    方法声明(参数) 异常类型)
		符号 *  0-多个任意字符
		符号 .. 用在方法参数中,表示任意个参数;用在包名后,表示当前及其子包路径
		符号 +  用在类名后,表示当前及其子类;用在接口后,表示当前接口及其实现类
		示例:
		execution(* com.mk.study.service.*.*(..))
		指定切入点为:定义在 service 包里的任意类的任意方法任意参数。
	5. Advice(通知/增强)  通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。

3.3.1 AOP实现

1. 导入Aspects依赖
		<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.13.RELEASE</version>
        </dependency>
	2. 在我们切入方法的这个类上 声明 @Aspect //代理对象
	3. 在切入方法上声明 Advice(通知)的类型 例如:@Before()
	4. 在第三步的基础上增加 切入点表达式,告诉Spring这个Before要在那个方法前调用 
		@Before("execution(* com.mk.study.service..*.*(..))")
	5. 在Spring的xml配置文件中 开启 自动代理
		<aop:aspectj-autoproxy proxy-target-class="true"/>

3.3.2 通知的五种表达式

1. 前置通知+切入点表达式
		@Before("execution(* com.mk.study.service..*.*(..))")
	2. 后置通知+ 切入点表达式
		@AfterReturning(value = "execution(* com.mk.study.service..*.update(..)))",returning = "result")
		result: 必须在通知方法中的形参加上Object result    代表的是 切入点方法的返回参数
	3. 异常通知+切入点表达式
		@AfterThrowing(value = "execution(* com.mk.study.service..*.update(..)))",throwing = "ex")
		ex:必须在通知方法中的形参加上Throwing ex  代表的是 切入点方法报错的异常可以捕获到
		 @AfterThrowing(value = "execution(* com.mk.study.service..*.update(..)))",throwing = "ex")
    public void exception(JoinPoint joinPoint,Throwable ex){
        System.out.println("异常通知");
        System.out.println("切入点的"+joinPoint.getSignature().getName()+"方法报"+ex.getMessage()+"错");
    }
	4. 最终通知+切入点表达式 无论是否出现异常都会被调用
		@After("execution(* com.mk.study.service..*.update(..)))") 
	5. 环绕通知+切入点表达式	
		@Around("execution(* com.mk.study.service..*.update(..)))")
    public Object round(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕通知前");
        //切入点方法
        Object proceed = joinPoint.proceed();
        System.out.println("环绕通知后");
        return proceed;
    }

3.3.3 XML文件配置AOP

<!--    XML 切入点表达式-->
    <aop:config>
<!--        ref 引用切面类-->
        <aop:aspect ref="myAspect2">
<!--            通知-->
            <aop:before method="before" pointcut="execution(* com.mk.study.aop.MyAspect2.*(..))"></aop:before>
        </aop:aspect>
    </aop:config>

四、Spring整合JDBC

1. 导入 spring-jdbc  mysql  c3p0 依赖
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
		<dependency>
		    <groupId>org.springframework</groupId>
		    <artifactId>spring-jdbc</artifactId>
		    <version>5.3.9</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.16</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>
	2. 
	ComboPooledDataSource c3p0=new ComboPooledDataSource();
        c3p0.setDriverClass("com.mysql.cj.jdbc.Driver");
        c3p0.setJdbcUrl("jdbc:mysql://localhost:3306/数据库名?serverTimezone=UTC&characterEncoding=UTF8&useUnicode=true&useSSL=false");
        c3p0.setUser("root");
        c3p0.setPassword("密码");
        JdbcTemplate jdbcTemplate = new JdbcTemplate(c3p0);
        String sql="insert into team() values(2,1)";
        int update = jdbcTemplate.update(sql);
        System.out.println(update);

4.1 整合JDBC2

1. 配置XML
		<!--    数据源-->
    <bean id="c3p0" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/数据库名?serverTimezone=UTC&amp;characterEncoding=UTF8&amp;useUnicode=true&amp;useSSL=false"/>
        <property name="user" value="root"/>
        <property name="password" value="密码"/>
    </bean>
	<!--    把数据源 交给 jdbcTemplate模板-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="c3p0"/>
    </bean>
	<!--    我们的Dao层继承了Spring提供的JdbcDaoSupport类,里面包含了jdbcTemplate,我们只需要把带有数据源的jdbcTemplate交给我们的Dao层即可-->
    <bean id="teamDao" class="com.mk.study.dao.TeamDao">
        <property name="jdbcTemplate" ref="jdbcTemplate"/>
    </bean>
	2. Dao层
		public class TeamDao  extends JdbcDaoSupport {
    public int insert(Team team){
        return this.getJdbcTemplate().update("insert into team values(?,?)",team.getId(),team.getName());
    }
}
	3. 测试
	public static void main(String[] args) {
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("application.xml");
        TeamDao teamDao = (TeamDao) applicationContext.getBean("teamDao");
        System.out.println(teamDao.insert(new Team(3, "张三")));
    }

4.2增删改查

1. 增加 / 批量增加
		public int insert(Team team){
        return this.getJdbcTemplate().update("insert into team values(?,?)",team.getId(),team.getName());
    }
	public int[] batchInsert(){
        List<Object[]> arg=new ArrayList();
        arg.add(new Object[]{值1,"值2"});
        arg.add(new Object[]{值1,"值2"});
        arg.add(new Object[]{值1,"值2"});
        return this.getJdbcTemplate().batchUpdate("insert into team values(?,?)",arg);
    }
	2. 删除
		public int insert(int id){
        	return this.getJdbcTemplate().update(sql删除语句,id);
	    }
	3.  更新
			public int insert(Team team){
        	return this.getJdbcTemplate().update(sql更新语句,team.参数);
	    }
	4. 查询
			//单值查找 方法1
		    public Team findById(int id){
		        // Spring 2.5 提供了一个便利的RowMapper实现-----BeanPropertyRowMapper
		        // *它可自动将一行数据映射到指定类的实例中 它首先将这个类实例化,然后通过名称匹配的方式,映射到属性中去。
		        BeanPropertyRowMapper<Team> teamBeanPropertyRowMapper = new BeanPropertyRowMapper<Team>(Team.class);
		        return this.getJdbcTemplate().queryForObject("select * from team where id=?",teamBeanPropertyRowMapper,id);
		    }
		    //单值查找 方法2
		    public Team findById2(int id){
			        return this.getJdbcTemplate().queryForObject("select * from team where id=?", new RowMapper<Team>() {
			            public Team mapRow(ResultSet resultSet, int i) throws SQLException {
			                Team team = new Team();
			                team.setId(resultSet.getInt("id"));
			                team.setName(resultSet.getString("name"));
			                return team;
			            }
			        }, id);
			}
	5.  查询所有
		public List<Team> findAll(){
	        //它可自动将一行数据映射到指定类的实例中 它首先将这个类实例化,然后通过名称匹配的方式,映射到属性中去。
	        BeanPropertyRowMapper<Team> teamBeanPropertyRowMapper = new BeanPropertyRowMapper<Team>(Team.class);
	        return getJdbcTemplate().query("select * from team",teamBeanPropertyRowMapper);
	    }
		//方法2
		public List<Team> findAll2(){
	        return getJdbcTemplate().query("select * from team", new RowMapper<Team>() {
	            public Team mapRow(ResultSet resultSet, int i) throws SQLException {
	                Team team = new Team();
	                team.setId(resultSet.getInt("id"));
	                team.setName(resultSet.getString("name"));
	                return team;
	            }
	        });
	    }
	6. 获取列数
		public int count(){
	        String sql="select * from team";
	        //实现了RowCallbackHandler接口,其中简单的实现了对结果集元数据的获取,包括行数、列数、列名、列的类型等信息
	        RowCountCallbackHandler rowCountCallbackHandler = new RowCountCallbackHandler();
	        getJdbcTemplate().query(sql,rowCountCallbackHandler);
	        System.out.println(rowCountCallbackHandler.getRowCount());
	        System.out.println(rowCountCallbackHandler.getColumnCount());
	        System.out.println(rowCountCallbackHandler.getColumnNames()[0]);
	        return rowCountCallbackHandler.getRowCount();
	    }
	    //方法2 获取单列计数
	    public int countCol(){
        String sql="select count(1) from team";
        return getJdbcTemplate().queryForObject(sql,Integer.class);
    }
    	//方法三 获取多列计数
    	public Map<String, Object> countCol(){
	        String sql="select count(1),count(2) from team";
	        return getJdbcTemplate().queryForMap(sql);
	    }

五、事务

5.1 事务的传播

1. PROPAGATION_REQUIRED 如果存在一个事务,则支持当前事务,如果没有事务则开启事务 (常用 增删改)
	2. PROPAGATION_SUPPORTS 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行 (常用 查)
	3. PROPAGATION_MANDATORY 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常
	
	配置:
		1. 在XML配置文件中写入
			<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
				<property name="dataSource" ref="dataSource"/> //数据源连接数据库的bean对象
			</bean>
		2. 注解开启事务 <tx:annotation-driven transaction-manager="transactionManager"/>
		3. 在方法上加上事务
			@Transactional(propagation=Propagation.REQUIRED,rollbackFor = {Exception.class})
		// XML方式配置事务
		<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
	        <property name="dataSource" ref="c3p0"/> //数据源连接数据库的bean对象
	    </bean>
	    <tx:advice id="transactionInterceptor" transaction-manager="transactionManager">//连接事务管理器
	        <tx:attributes>
	            <tx:method name="add*" propagation="REQUIRED" rollback-for="Exception"/>
	        </tx:attributes>
	    </tx:advice>
	    <aop:config>
	        <aop:pointcut id="pt" expression="execution(* com.mk.study.service..*.*(..))"/>
	        <aop:advisor advice-ref="transactionInterceptor" pointcut-ref="pt"></aop:advisor>
	    </aop:config>

5.2 事务的隔离级别

隔离级别
ISOLATION_DEFAULT这是个 PlatfromTransactionManager 默认的隔离级别,
ISOLATION_READ_UNCOMMITTED它允许另外一个事务可以看 到这个事务未提交的数据。这种隔离级别会产生脏读, 不可重复读和幻像读。
ISOLATION_DEFAULT这是个 PlatfromTransactionManager 默认的隔离级别, 使用数据库默认的事务隔离级别。
ISOLATION_READ_COMMITTED保证一个事务修改的数据提交后才能被另外一个事务读 取。另外一个事务不能读取该事务未提交的数据。
ISOLATION_SERIALIZABLE串行化。不存在并发问题。

相关文章