spring 个人记录
一.遇到的问题
1:代码高耦合
public class EmployeeServiceImpl {
//依赖DAO
private IEmployeeDAO employeeDAO = new EmployeeDAOImpl();
}
问题:若把IEmployeeDAO的实现改成:EmployeeMapper.
----->简单工厂设计模式
----->把创建对象的职责交给工厂来管理.
到底工厂创建哪一个类的对象呢?我们是配置在配置文件中的(properties/xml).
2:业务代码中处理事务的繁琐问题
对于事务操作,代码冗余,必须在所有需要控制事务的地方,手动用代码完成几乎完全相同的事务控制逻辑,开发效率低下,并且难以方便的处理事务嵌套需求。
如何降低业务逻辑部分之间耦合度,提高程序的可重用性,同时提高开发的效率!—> AOP
主要分散在方法前后,在真正的业务操作前后的代码,我们可以使用面向切面编程来解决.
EmployeeService{
public void save(...){
//开启事务
dao.save(...);
//提交事务
}
public void update(...){
//开启事务
dao.update(...);
//提交事务
}
}
3:使用表现层和持久层框架的繁琐步骤
二.Spring介绍
什么是spring:
1、Spring是一个轻量级的DI/IoC和AOP容器的开源框架,来源于Rod Johnson 在其著作《Expert one on one J2EE design and development》中阐述的部分理念和原型衍生而来。
2、Spring提倡以”最少侵入”的方式来管理应用中的代码,这意味着我们可以随时安装或卸载Spring。
使用范围:任何Java应用
Spring根本使命:简化Java开发
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架,致力于构建轻量级的JavaEE应用
Spring中常见术语:
1、应用程序:是能完成我们所需要功能的成品,比如购物网站、OA系统。
2、框架:是能完成一定功能的半成品,比如我们可以使用框架进行购物网站开发;框架做一部分功能,我们自己做一部分功能,这样应用程序就创建出来了。而且框架规定了你在开发应用程序时的整体架构,提供了一些基础功能,还规定了类和对象的如何创建、如何协作等,从而简化我们开发,让我们专注于业务逻辑开发。
3、非侵入式设计:从框架角度可以这样理解,无需继承框架提供的类,这种设计就可以看作是非侵入式设计,如果继承了这些框架类,就是侵入设计,如果以后想更换框架之前写过的代码几乎无法重用,如果非侵入式设计则之前写过的代码仍然可以继续使用。
4、轻量级及重量级:轻量级是相对于重量级而言的,轻量级一般就是非入侵性的、所依赖的东西非常少、资源占用非常少、部署简单等等,其实就是比较容易使用,而重量级正好相反。
5、POJO:POJO(Plain Old Java Objects)简单的Java对象,它可以包含业务逻辑或持久化逻辑,但不担当任何特殊角色且不继承或不实现任何其它Java框架的类或接口。
6、容器(Container):在日常生活中容器就是一种盛放东西的器具,从程序设计角度看就是装对象的的对象,因为存在放入、拿出等操作,所以容器还要管理对象的生命周期。
Spring的优势:
1.低侵入/低耦合(降低组件之间的耦合度,实现软件各层之间的解耦。)
2.声明式事务管理
3.方便集成其他框架
4.降低JavaEE开发难度
5.Spring框架中包括JavaEE三层的每一层的解决方案 (一站式)
Spring能帮我们做什么:
①.Spring能帮我们根据配置文件创建及组装对象之间的依赖关系。
②.Spring 面向切面编程能帮助我们无耦合的实现日志记录,性能统计,安全控制。
③.Spring能非常简单的帮我们管理数据库事务。
④.Spring还提供了与第三方数据访问框架(如Hibernate、JPA)无缝集成,而且自己也提供了一套JDBC访问模板来方便数据库访问。
⑤.Spring还提供与第三方Web(如Struts1、JSF)框架无缝集成,而且自己也提供了一套Spring MVC框架,来方便web层搭建。
⑥.Spring能方便的与Java EE(如Java Mail、任务调度)整合,与更多技术整合(比如缓存框架)。
Spring框架版本:
Spring2.5:
拥抱驱动编程。
支持SimpleJdbcTemplate的命名参数操作。
Spring3.x:
不再支持JDK1.4;
全面支持泛型。
支持SpEL.
支持WebService的OXM。
Spring4.x:
支持Java8,支持JavaEE6规范。
泛型限定式依赖注入。
对Hibernate4的集成和事务提供更好的管理方案。
Spring框架包分析:
现在都提倡使用Maven去下载Spring的jar:
下载地址:http://repo.spring.io/libs-release-local/org/springframework/spring/
spring-framework-4.x.RELEASE:Spring核心组件。
docs: Spring开发、帮助文档。
libs: Spring核心组件的。jar包、源代码、文档。
schema: Spring配置文件的schema约束文件。
spring-framework-3.x.RELEASE-dependencies:Spring依赖的第三方组件。
包含了各大开源组织提供的依赖jar。比如apache common下的:dbcp.jar pool.jar logging.jar
三.IoC和DI简单介绍
IoC:Inverse of Control(控制反转):
读作“控制反转”,更好理解,不是什么技术,而是一种设计思想,好比于MVC。就是将原本在程序中手动创建对象的控制权,交由Spring框架来管理。
正控:若调用者需要使用某个对象,其自身就得负责该对象的创建。
反控:调用者只管负责从Spring容器中获取需要使用的对象,不关心对象的创建过程,也就是把创建对象的控制权反转给了Spring框架。
在这里完美地体现了好莱坞法则(Don’t call me ,I’ll call you)。
DI:Dependency Injection(依赖注入):
从字面上分析:
IoC:指将对象的创建权,反转给了Spring容器;
DI :指Spring创建对象的过程中,将对象依赖属性(简单值,集合,对象)通过配置设值给该对象。
IoC和DI其实是同一个概念的不同角度描述,DI相对IoC而言,明确描述了“被注入对象依赖IoC容器配置依赖对象”。
Container:
容器,在生活中容器就是一种盛放东西的器皿,从程序设计角度看作是装对象的对象,因为存在对对象的存入、取出等操作,所以容器还要管理对象的生命周期。
四.案例 helloworld
//准备jar包(核心包)
spring-beans-4.2.4.RELEASE.jar
spring-core-4.2.4.RELEASE.jar
//报错再添加:
commons.logging-1.2.jar
创建applicationContext.xml
通过import分模块引入各个模块的bean.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.xsd">
<!--引入各个模块的bean.xml-->
<import resource="classpath:gzcPro/spring/_01helloSpring/hello.xml"></import>
<import resource="classpath:gzcPro/spring/_02instance/content/App_context.xml"></import>
</beans>
创建hello.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.xsd">
<!-- Ioc: 控制反转 将class交给spring容器管理-->
<bean id="helloWorld" class="gzcPro.spring._01helloSpring.HelloWorld">
<!--DI:依赖注入-->
<property name="name" value="gzc"></property>
</bean>
</beans>
创建App_context.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.xsd">
<!-- Ioc: 控制反转 将class交给spring容器管理
lazy-init是否在打开spring容器时就加载 false为不延时加载 true为延时加载
一般默认为false启动初始化所有的bean 不写-->
<bean id="someBean" class="gzcPro.spring._02instance.SomeBean" lazy-init="false">
</bean>
</beans>
创建HelloWorld.java
package gzcPro.spring._01helloSpring;
import lombok.*;
/**
* Created by Administrator on 2019/8/1.
*/
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Setter
@Getter
public class HelloWorld {
private String name;
public void sayHello(){
System.out.println("hello:"+ name);
}
}
创建SomeBean.java
package gzcPro.spring._02instance;
import lombok.*;
/**
* Created by Administrator on 2019/8/1.
*/
@ToString
@Setter
@Getter
public class SomeBean {
public SomeBean(){
System.out.println("bean被创建了");
}
}
创建测试代码APP.java
@Test
public void Ioc(){
//加载spring容器配置文件
Resource resource = new ClassPathResource("applicationContext.xml");
//启动spring容器
BeanFactory factory=new XmlBeanFactory(resource);
//way-01 getBean 需要强转(不推荐) 容器中可能有多个相同id
//HelloWorld helloWorld=(HelloWorld) factory.getBean("helloWorld");
//way-02 getBean 容器中可能有多个相同class(不推荐)
//HelloWorld helloWorld= factory.getBean(HelloWorld.class);
//way-03 getBean (推荐)
HelloWorld helloWorld= factory.getBean("helloWorld",HelloWorld.class);
System.out.println(helloWorld);
}
bean被创建了
HelloWorld(name=gzc)
applicationContext.xml 中包含App_context.xml
如下模拟所说,会获取空的构造函数,所以下面代码执行了
public SomeBean(){ //空的构造函数
System.out.println("bean被创建了");
}
Spring管理bean的原理:
什么是BeanFactory:
Spring最基本的接口,表示Spring容器——生产bean对象的工厂,负责配置,创建和管理bean。
备注:bean是Spring管理的单位,在Spring中一切都是bean.
深入Spring管理bean的原理:
1、通过Resource对象加载配置文件;
2、解析配置文件,得到指定名称的bean;
3、解析bean元素,id作为bean的名字,class用于反射得到bean的实例:
模拟spring容器实现:
//模拟spring容器实现
@Test
public void test() throws Exception {
//模拟xml数据
String className = "gzcPro.spring._01helloSpring.HelloWorld";
String propertyName = "name";
String propertyValue = "桂志超";
//模拟spring操作:IOC 内省
Class<?> aClass = Class.forName(className);
//获取空参构造器
Constructor<?> declaredConstructors = aClass.getDeclaredConstructor();
//允许访问
declaredConstructors.setAccessible(true);
HelloWorld helloWorld = (HelloWorld) declaredConstructors.newInstance();
//通过内省 赋值DI
BeanInfo beanInfo = Introspector.getBeanInfo(aClass, Object.class);
//获取描述器
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd:propertyDescriptors) {
if (propertyName.equals(pd.getName())){
pd.getWriteMethod().invoke(helloWorld,propertyValue);
}
}
System.out.println(helloWorld);
helloWorld.sayHello();
}
HelloWorld(name=gzc)
hello:gzc
说到底,Ioc就是在配置文件中 添加bean对象, DI就是在对象在添加属性值
单元测试说明
junit4.12 hamcrest-core-1.3.jar
junit-4.12.jar
spring-test-4.2.4.RELEASE.jar. //单元测试
spring-context-4.2.4.RELEASE.jar. //
spring-aop-4.2.4.RELEASE.jar. //
spring-expression-4.2.4.RELEASE.jar. //
将 spring容器拿出来,不用每次都创建spring容器
/**
* Created by gzc on 2019/8/1.
*/
@RunWith(SpringJUnit4ClassRunner.class)//引入spring单元测试
@ContextConfiguration("classpath:applicationContext.xml")//引入配置文件
public class App {
//自动从spring容器中获取这个bean
@Autowired
private HelloWorld helloWorld;
@Test
public void test1() throws Exception {
System.out.println(helloWorld);
}
}
@Autowired 自动从spring容器中获取这个bean(bean是存在的)
@RunWith(SpringJUnit4ClassRunner.class)//引入spring单元测试
@ContextConfiguration(“classpath:applicationContext.xml”)//引入配置文件
@Autowired
private HelloWorld helloWorld;
取代了----------->
//加载spring容器配置文件
Resource resource = new ClassPathResource(“applicationContext.xml”);
//启动spring容器
BeanFactory factory=new XmlBeanFactory(resource);
HelloWorld helloWorld= factory.getBean(“helloWorld”,HelloWorld.class);
IoC容器:BeanFactory和ApplicationContext对象
BeanFactory:是Spring中最底层的接口,只提供了最简单的IoC功能,负责配置,创建和管理bean。
在应用中,一般不使用BeanFactory,而推荐使用ApplicationContext(应用上下文),原因如下。
BeanFactory和ApplicationContext的区别:
1、ApplicationContext继承了BeanFactory,拥有了基本的IoC功能;
2、除此之外,ApplicationContext还提供了以下的功能:
①、支持国际化;
②、支持消息机制;
③、支持统一的资源加载;
④、支持AOP功能;
package gzcPro.spring._02instance.content;
import ...
/**
* Created by gzc on 2019/8/1.
*/
//@RunWith(SpringJUnit4ClassRunner.class)//引入单元测试
//@ContextConfiguration("classpath:applicationContext.xml")//引入配置文件
public class App {
//自动从spring容器中获取这个bean
// @Autowired
// private ApplicationContext context;
@Test
public void testAppCtx(){
ApplicationContext context = new ClassPathXmlApplicationContext("gzcPro/spring/_02instance/content/App_context.xml");
SomeBean someBean1=context.getBean("someBean",SomeBean.class);
SomeBean someBean2=context.getBean("someBean",SomeBean.class);
System.out.println(someBean1==someBean2);
}
}
ApplicationContext context = new ClassPathXmlApplicationContext(“gzcPro/spring/_02instance/content/App_context.xml”);
开启App_context.xml spring容器中的所有bean
---------------------------------------------------》换成
@ContextConfiguration(“App_context.xml”)//引入配置文件
或者(@ContextConfiguration(“classpath:applicationContext.xml”)//引入配置文件)
@Autowired
private ApplicationContext context;
/**
* Created by gzc on 2019/8/1.
*/
@RunWith(SpringJUnit4ClassRunner.class)//引入单元测试
//@ContextConfiguration("classpath:applicationContext.xml")//引入配置文件
@ContextConfiguration("App_context.xml")//引入配置文件
public class App {
//自动从spring容器中获取这个bean
@Autowired
private ApplicationContext context;
@Test
public void testAppCtx(){
SomeBean someBean1=context.getBean("someBean",SomeBean.class);
SomeBean someBean2=context.getBean("someBean",SomeBean.class);
System.out.println(someBean1==someBean2);
}
}
五.bean
bean的实例化方式:
①.构造器实例化(无参数构造器),最标准,使用最多。
②.静态工厂方法实例化:解决系统遗留问题
<!-- Ioc: 控制反转 将class交给spring容器管理-->
<!-- 使用静态工厂 -->
<bean id="someBean1" class="gzcPro.spring._02instance.static_factory.SomeBeanFactory"
factory-method="getObject">
</bean>
//静态工厂
public class SomeBeanFactory {
public static SomeBean getObject(){
return new SomeBean();
}
}
//自动从spring容器中获取这个bean
@Autowired
private ApplicationContext context;
@Test
public void testAppCtx() throws Exception{
context.getBean("someBean1");
}
@Test
public void testOld() throws Exception{
SomeBean someBean=SomeBeanFactory.getObject();
}
③.实例工厂方法实例化:解决系统遗留问题
<!-- 使用实例工厂-->
<bean id="factory" class="gzcPro.spring._02instance.factory.SomeBeanFactory" />
<bean id="someBean" factory-bean="factory" factory-method="getObject" />
public class SomeBeanFactory implements FactoryBean<SomeBean>{
public SomeBean getObject(){
return new SomeBean();
}
}
//自动从spring容器中获取这个bean
@Autowired
private ApplicationContext context;
@Test
public void testAppCtx() throws Exception{
System.out.println(context.getBean("someBean"));
}
@Test
public void testOld() throws Exception{
SomeBeanFactory factory=new SomeBeanFactory();
SomeBean someBean= factory.getObject();
}
④.实现FactoryBean接口实例化:实例工厂变种:
集成其他框架使用:SqlSessionFactoryBean和MapperFactoryBean
<!-- FactroyBean创建 -->
<bean id="someBean" class="gzcPro.spring._02instance.instance_factory.SomeBeanFactory" />
public class SomeBeanFactory implements FactoryBean<SomeBean>{
public SomeBean getObject(){
return new SomeBean();
}
@Override
public Class<?> getObjectType() {
return null;
}
//bean是否单例
@Override
public boolean isSingleton() {
return false;
}
}
bean作用域
1.单例
2.多例
bean对象的作用域(bean对象可以存活的时间):
<bean id="" class="" scope="作用域"/>
singleton: 单例 ,在Spring IoC容器中仅存在一个Bean实例 (默认的scope)
prototype: 多例 ,每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时 ,相当于执行new XxxBean():不会在容器启动时创建对象
request: 用于web开发,将Bean放入request范围 ,request.setAttribute(“xxx”) , 在同一个request 获得同一个Bean
session: 用于web开发,将Bean 放入Session范围,在同一个Session 获得同一个Bean
globalSession: 一般用于Porlet应用环境 , 分布式系统存在全局session概念(单点登录),如果不是porlet环境,globalSession 等同于Session
在开发中主要使用 scope=“singleton”、 scope=“prototype”
对于Struts2中的Action使用prototype类型,其他使用singleton
Spring容器会管理Action对象的创建,此时把Action的作用域设置为prototype.
bean的生命周期 ( init destroy )
<?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.xsd"
default-init-method="init"
default-destroy-method="destroy">
<!-- Ioc: 控制反转 将class交给spring容_03scopecope="prototype"多例-->
<!-- Ioc: 控制反转 将class交给spring容器管理
init-method="init"
destroy-method="destroy"-->
<bean id="someBean" class="gzcPro.spring._03scope.SomeBean">
</bean>
</beans>
public class SomeBean {
public SomeBean(){
System.out.println("bean被创建了");
}
public void init() {
System.out.println("初始化");
}
public void destroy() {
System.out.println("被销毁");
}
public void doWorlk() {
System.out.println("工作");
}
}
@RunWith(SpringJUnit4ClassRunner.class)//引入单元测试
@ContextConfiguration("App_context.xml")//引入配置文件
public class App {
//自动从spring容器中获取这个bean
@Autowired
private ApplicationContext context;
@Test
public void testAppCtx(){
SomeBean someBean1=context.getBean("someBean",SomeBean.class);
SomeBean someBean2=context.getBean("someBean",SomeBean.class);
System.out.println(someBean1==someBean2);
someBean1.doWorlk();
}
}
bean被创建了
初始化
true
工作
被销毁
六.依赖注入DI及简化Ioc
依赖注入
DI:Dependency Injection: 依赖注入
从字面上分析:
IoC:指将对象的创建权,反转到Spring容器;
DI :指Spring创建对象的过程中,将对象依赖属性通过配置进行注入
其实它们是同一个概念的不同角度描述。DI相对IoC而言,明确描述了“被注入对象依赖IoC容器配置依赖对象”。
对象注入的方式:
1):自动装配(了解)
2):手动注入(重点)
3):注解注入(重点)
通过哪些手段可以注入:
1):可以通过属性注入(setter方法).
2):通过构造器注入.
3):通过SpEL注入.
注入的时候,可有注入哪些类型的值.
1:简单类型(单一的值,使用value属性).
2:复合类型(JavaBean类型,使用ref属性)
3:集合类型(多个值,使用对应的集合标签)
使用setter注入(属性注入):
1,使用bean元素的property子元素设置;
1,简单类型值,直接使用value赋值;
2,复合类型,使用ref赋值;
3,集合类型,直接使用对应的集合类型元素即可。
2,spring通过属性的setter方法注入值;
3,在配置文件中配置的值都是string,spring可以自动的完成类型的转换
4,属性的设置值是在init方法执行之前完成的
5,改进spring的测试,直接在测试类里面注入需要测试的对象
使用DataSource(druid)的案例,给数据源注入Property中的数据.
准备:
MySQL驱动包和Druid连接池包.
druid-1.0.15.jar
mysql-connector-java-8.0.15.jar
<context:property-placeholder location="db.properties" system-properties-mode="NEVER">
</context:property-placeholder>
<!-- 配置数据源-->
<bean id="dataSurce" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="driverClassName" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</bean>
db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
username=root
password=123456
自动装配方式(不推荐):
设置:<bean />
元素的:autowire属性:
<bean id="somebean" class="SomeBean全限定名" autowire="byType"/>
autowire属性:让spring按照一定的方式自己去找合适的对象,并完成DI
- default:不要自动注入
- no:不要自动注入
- byName:按照名字注入(按照属性的名字在spring中找bean) factory.getBean(“属性的名字”)
- byType:按照依赖对象的类型注入(factory.getBean(属性的类型))
- constructor:按照对象的构造器上面的参数类型注入
注意:
1,如果按照byName自动注入,要求所有的属性名字和id的名字必须保证一种规范的命名方式;
2,如果按照byType注入,如果spring中同一个类型有多个实例–>报bean不是唯一类型错误;
注解注入
<!-- 开始DI注解解析程序-->
<context:annotation-config/>
Spring提供的装配标签:
Autowired和Qualifier标签:
1.通过@Autowired标签可以让Spring自动的把属性需要的对象从Spring容器找出来,并注入给该属性。
2.第三方程序:Spring3.0之前,需要手动配置@Autowired解析注解程序,Spring就会自动的加入针对@Autowired标签的解析程序。从Spring3.0开始,可以不再需要改配置了。如果不在Spring的测试环境中,也找到@Autowired的解析代码,此时页必须配置.
3.@Autowired标签贴在字段或者setter方法上。
4.@Autowired可以同时为一个属性注入多个对象。
public void setXxx(OtherBean1 other1,OtherBean2 other2) {}
5.使用@Autowired标签可以注入Spring内置的重要对象,比如BeanFactory,ApplicationContext。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class SpringTest {
@Autowired
private ApplicationContext ctx;
}
6.默认情况下@Autowired标签必须要能找到对应的对象,否则报错。不过,可使用required=false来避免该问题:@Autowired(required=false)
7.@Autowired找bean的方式:
1)、首先按照依赖对象的类型找,如果找到则使用setter方法或者字段直接注入;
2)、如果在Spring上下文中找到多个匹配的类型,再按照名字去找,如果没有匹配则报错;
3)、可以通过使用@Qualifier(“otherBean”)标签来规定依赖对象按照bean的id+类型去找;
@ToString
public class SomeBean {
@Autowired
@Qualifier("otherBean") //<bean id=otherBean
private OtherBean otherBean;
@Resource
private OtherBean2 otherBean2;
}
JavaEE提供的装配标签:
@Resource标签:
1,@Resource标签是JavaEE规范的标签;
2,@Resource标签也可以作用于字段或者setter方法;, 效果是跟autowired是一样的
3,也可以使用@Resource标签注入一些spring内置的重要对象,比如BeanFactory.ApplicationContext;
4,@Resource必须要求有匹配的对象;
5,context:annotation-config/既引入了@Autowired标签的解析器,也引入了@Resource的解析器;
6,@Resource标签找bean的方式:
1),首先按照名字去找,如果找到,就使用setter或者字段注入;
2),如果按照名字找不到,再按照类型去找,但如果找到多个匹配类型,报错;
3),可以直接使用name属性指定bean的名称;但是,如果指定的name,就只能按照name去找,如果找不到,就不会再按照类型去找
@Resource(name="otherBean2")
private OtherBean2 other2;
等价于
@Autowired
@Qualifier("otherBean2")
private OtherBean2 other2;
@Autowired VS @Resource:
1,@Autowired:是Spring定义的标签,所以不太稳定,并且对象和spring框架关联(Spring对我们的代码有侵入);
2,@Resouce:是J2EE的规范,所以稳定,在J2EE规范容器中也能正常使用;
在开发中两个注解都是一样的作用,所有选择谁都可以,完全取决于项目经理的选择
使用标签简化IoC:
1.使用标签来完成IoC,就必须有IoC标签的解析器:
使用context:component-scan来扫描spring需要管理的bean
base-package就告诉spring,去哪些包及其子包里去扫描bean,如果有多个包需要被扫描;只需要用逗号隔开多个包即可
<context:component-scan base-package=“com._520it.oa.dao,cn.com._520it.service” />
2.标注Bean的注解:@Component
默认情况,直接使用类的名字(首字母小写作为bean的名字)
如果要修改bean的名称;直接使用value属性来重新定义bean的名称
@Component(“otherbean”)
public class OtherBean {}
3.使用@Component的限制:
1),不能运用到静态工厂方法和实例工厂方法,但是可以使用到FactoryBean;
2),对于没有源代码的类(框架内部的预定义类),只能用XML配置;
4.bean组件版型标签
bean组件版型:
@Service用于标注业务层组件、
@Controller用于标注控制层组件(如struts中的action)
@Repository用于标注数据访问组件,即DAO组件。
@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
5.指定bean的作用域:@Scope(“prototype”)
6.初始化和销毁方法
@PostConstruct
public void init() {
相当于
@PreDestroy
public void destory() {
相当于
7.选用xml还是注解:
1),Annotation:使用方便,XML文件很小,但是,依赖关系又重新回到了代码当中;
2),XML:使用稍微复杂,但是,代码很干净,代码不会很任何框架产生关系;XML安全;
两种方式都必须掌握;
<!-- 开始DI注解解析程序 如果不在Spring的测试环境中,也找到@Autowired的解析代码,此时页必须配置-->
<context:annotation-config/>
<!-- 限定属于哪个包中-->
<context:component-scan base-package="com.gjd.domain"></context:component-scan>
@Component
public class User {
private Long id;
private String name;
private BigDecimal salary;
// @DateTimeFormat(pattern = "yyyy-MM-dd")
private Date hiredate;
public void sayhello(){
System.out.println("gjd");
}
}
@Autowired
private User user;
七.代理及AOP简介、配置
增强功能
事务开启,提交和回滚代码
public class TransactionManager {
public void begin(){
System.out.println("开启一个事务");
}
public void commit(){
System.out.println("提交一个事务");
}
public void rollback(){
System.out.println("回滚一个事务");
}
}
public class EmployeeServiceImpl implements IEmployeeService {
@Override
public void save() {
System.out.println(1/0);
System.out.println("保存一个员工");
}
@Override
public void update() {
System.out.println("更新一个员工");
}
}
装饰设计模式
装饰设计模式:在不必改变源代码基础上,动态地扩展一个对象的功能。
它是通过创建一个包装对象,也就是包裹真实的对象。
说的直白点,就是对已有对象进行功能增强!
1.装饰模式思想
2.装饰模式实现
得定义一个类(EmployeeService的包装类):
目的:增强EmployeeServiceImpl中的save和update方法.
做什么增强:
1:在调用save方法之前:开启事务:
2:正常调用完save方法之后:提交事务:
3:如果调用save方法出现异常:回滚事务:
//参数构造函数 !importent
@AllArgsConstructor
public class EmployeeServiceWrapper implements IEmployeeService{
private IEmployeeService employeeService;
private TransactionManager transactionManager;
@Override
public void save() {
try {
transactionManager.begin();
employeeService.save();
transactionManager.commit();
}catch (Exception e){
e.printStackTrace();
transactionManager.rollback();
}
}
@Override
public void update() {
try {
transactionManager.begin();
employeeService.update();
transactionManager.commit();
}catch (Exception e){
e.printStackTrace();
transactionManager.rollback();
}
}
}
public class App {
@Test
public void testApp() throws Exception {
IEmployeeService service=new EmployeeServiceImpl();
TransactionManager transactionManager=new TransactionManager();
EmployeeServiceWrapper wrapper=new EmployeeServiceWrapper(service,transactionManager);
wrapper.save();
wrapper.update();
}
}
装饰设计模式在这里存在的问题
1):确实可以这个解决问题.
2):会暴露真实对象–不安全.
3):需要为每一个需要增强的类定义一个包装类
静态代理
//静态代理类
public class EmployeeServiceStaticProxy implements IEmployeeService{
//没有上面的构造函数,需要添加setter方法 !importent
@Setter
private IEmployeeService target;//真实对象
@Setter
private TransactionManager transactionManager;
@Override
public void save() {
try {
transactionManager.begin();
target.save();
transactionManager.commit();
}catch (Exception e){
e.printStackTrace();
transactionManager.rollback(e);
}
}
@Override
public void update() {
try {
transactionManager.begin();
target.update();
transactionManager.commit();
}catch (Exception e){
e.printStackTrace();
transactionManager.rollback(e);
}
}
}
<!-- 事务代理-->
<bean id="transactionManager" class="gzcPro.spring._09._staticproxy.manager.TransactionManager"/>
<!-- 静态代理-->
<bean id="employeeServiceStaticProxy" class="gzcPro.spring._09._staticproxy.proxy.EmployeeServiceStaticProxy">
<property name="transactionManager" ref="transactionManager"></property>
<property name="target">
<bean id="employeeService" class="gzcPro.spring._09._staticproxy.service.EmployeeServiceImpl"/>
</property>
</bean>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("App-context09.xml")
public class App {
@Autowired
private IEmployeeService proxy;
@Test
public void test() throws Exception {
proxy.save();
}
}
静态代理(proxy):在程序运行前就已经存在代理类的字节码文件,代理对象和真实对象的关系在程序运行前就确定了。
静态代理优缺点:
优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性。
缺点:
1.代理对象的某个接口只服务于某一种类型的对象,也就是说每一个真实对象都得创建一个代理对象。
2.如果需要代理的方法很多,则要为每一种方法都进行代理处理。
3.如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
如果期望一个代理类就可以代理多个真实对象------->动态代理.
JDK动态代理
/**
* Created by gzc on 2019/8/5.
*/
public class TransactionHandler implements InvocationHandler {
@Setter
private TransactionManager manager;
@Setter
private Object target;//真是对象
//获取代理对象
public <T> T getProxy(){
System.out.println(target.getClass());
return (T) Proxy.newProxyInstance(
TransactionHandler.class.getClassLoader(),
target.getClass().getInterfaces(), this);
}
/**
* h.invoke (h:this)
*实现代理功能的方法
*proxy 代理的对象
* method 代理方法 update 或者save 当前执行的代理方法
* args 代理方法的参数
*
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object ret = null;
//事务的开启
try {
manager.begin();
ret = method.invoke(target,args);
manager.commit();
}catch (Exception e) {
e.printStackTrace();
manager.rollback(e);
}
return ret;
}
}
<!-- 事务代理-->
<bean id="transactionManager" class="gzcPro.spring._09._staticproxy.manager.TransactionManager"/>
<!-- 静态代理-->
<bean id="transactionHandler" class="gzcPro.spring._09.proxyHander.TransactionHandler">
<property name="manager" ref="transactionManager"></property>
<property name="target">
<!--private Object target;//真对象-->
<bean class="gzcPro.spring._09._staticproxy.service.EmployeeServiceImpl"/>
</property>
</bean>
@Autowired
private TransactionHandler handler;
@Test
public void testSave() throws Exception {
IEmployeeService proxy = handler.getProxy();
System.out.println(proxy.getClass());
proxy.save();
}
动态代理类是在程序运行期间由JVM通过反射等机制动态的生成的,所以不存在代理类的字节码文件。代理对象和真实对象的关系是在程序运行事情才确定的。
JDK动态代理API分析:(只能对接口进行代理)
1、java.lang.reflect.Proxy 类:
Java 动态代理机制生成的所有动态代理类的父类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。
主要方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler hanlder)
方法职责:为指定类加载器、一组接口及调用处理器生成动态代理类实例
参数:
loader :类加载器
interfaces :模拟的接口
hanlder :代理执行处理器
返回:动态生成的代理对象
2、java.lang.reflect.InvocationHandler接口:
public Object invoke(Object proxy, Method method, Object[] args)
方法职责:负责集中处理动态代理类上的所有方法调用
参数:
proxy :生成的代理对象
method :当前调用的真实方法对象
args :当前调用方法的实参
返回: 真实方法的返回结果
jdk动态代理操作步骤
① 实现InvocationHandler接口,创建自己增强代码的处理器。
② 给Proxy类提供ClassLoader对象和代理接口类型数组,创建动态代理对象。
③ 在处理器中实现增强操作。
JDK动态代理存在的问题:
JDK动态代理:
1,代理的对象必须要实现接口;
2,需要为每个对象创建代理对象;
3,动态代理的最小单位是类(所有类中的方法都会被处理);
CGLIB动态代理
public class TransactionHandler implements org.springframework.cglib.proxy.InvocationHandler {
@Setter
private TransactionManager manager;
@Setter
private Object target;//真是对象
//获取代理对象
public <T> T getProxy(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return (T) enhancer.create();
}
/**
* h.invoke (h:this)
*实现代理功能的方法
*proxy 代理的对象
* method 代理方法 update 或者save 当前执行的代理方法
* args 代理方法的参数
*
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object ret = null;
//事务的开启
try {
manager.begin();
ret = method.invoke(target,args);
manager.commit();
}catch (Exception e) {
e.printStackTrace();
manager.rollback(e);
}
return ret;
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("App-context09.xml")
public class App {
@Autowired
private TransactionHandler handler;
@Test
public void testSave() throws Exception {
IEmployeeService proxy = handler.getProxy();
//class gzcPro._gzc01._09._staticproxy.service.EmployeeServiceImpl$$EnhancerByCGLIB$$333160c2
System.out.println(proxy.getClass());
proxy.save();
}
@Test
public void testUpdate() throws Exception {
IEmployeeService proxy = handler.getProxy();
//class gzcPro._gzc01._09._staticproxy.service.EmployeeServiceImpl$$EnhancerByCGLIB$$333160c2
System.out.println(proxy.getClass());
proxy.update();
}
}
原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
CGLIB代理总结:
1,CGLIB可以生成目标类的子类,并重写父类非final修饰符的方法。
2,要求类不能是final的,要拦截的方法要是非final、非static、非private的。
3,动态代理的最小单位是类(所有类中的方法都会被处理);
在Spring中:
若目标对象实现了若干接口,Spring就会使用JDK动态代理。
若目标对象没有实现任何接口,Spring就使用CGLIB库生成目标对象的子类。
对接口创建代理优于对类创建代理,因为会产生更加松耦合的系统,也更符合面向接口编程规。
cglib和javassist代理的机制都是一样的,都是通过继承实现的.
在上述的操作中,已经实现了动态的为目标对象做功能增强?
为什么还要学Spring的AOP呢?
原因之一是:在为N个service提供代理的时候,我们需要在xml中配置N次
AOP
package gzcPro.spring._09._staticproxy.manager;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* Created by gzc on 2019/8/4.
*/
@Component
@Aspect
public class TransactionManager {
@Pointcut("execution(* gzcPro.spring._09._staticproxy.service.*Service.*(..))")
public void pc(){
}
// @Before("pc()")
public void begin(){
System.out.println("开启一个事务");
}
// @AfterReturning("pc()")
public void commit(){
System.out.println("提交一个事务");
}
// @AfterThrowing(value = "pc()",throwing = "ex")
public void rollback(Throwable ex){
System.out.println("回滚一个事务"+ex.getMessage());
}
// @After("pc()")
public void close(){
System.out.println("关闭一个事务");
}
@Around("pc()")//@Before("pc()") @AfterReturning("pc()") @AfterThrowing(value = "pc()",throwing = "ex")@After("pc()")
public Object allInOne(ProceedingJoinPoint point){
Object ret = null ;
try {
begin();
ret = point.proceed();
commit();
} catch (Throwable throwable) {
rollback(throwable);
} finally {
close();
}
return ret;
}
}
AOP的目的:
AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,
便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
说人话:把业务方法中与业务无关的操作抽离到不同的对象的方法中,最后使用动态代理的方式组合起来
AOP的优势:
降低模块的耦合度、使系统容易扩展、更好的代码复用性.
Spring的AOP使用动态代理实现:
如果一个类实现了接口,那么spring就使用JDK的动态代理完成AOP;
如果一个类没有实现接口,那么spring就是用cglib完成AOP;
AOP当中的概念:
1、切入点(Pointcut):在哪些类,哪些方法上切入(where);: [在employeServiceImpl 类—>save方法]
2、增强(Advice): 早期翻译为通知,在方法执行的什么时机(when:方法前/方法后/方法前后)做什么(what:增强的功能);[在save方法执行前(when),开启事务(what)]
3、切面(Aspect): 切面=切入点+增强,通俗点就是:在什么时机,什么地点,做什么增强!
[在employeServiceImpl 类—>save方法(where)执行前(when)做开启事务操作(what)]
4、织入(Weaving): 把切面加入到对象,并创建出代理对象的过程。(该过程由Spring来完成)。
XML配置AOP
Spring配置AOP的准备工作:
1).引入Spring AOP开发依赖的jar包:
spring-aop-4.2.4.RELEASE.jar
aopalliance-1.0.0.jar
aspectjweaver-1.8.7.jar
各种不同的增强:
aop:before(前置增强):在方法执行之前执行增强;
aop:after-returning(后置增强):在方法正常执行完成之后执行增强(中间没有遇到任何异常);
aop:after-throwing(异常增强):在方法抛出异常退出时执行增强代码;
aop:after(最终增强):在方法执行之后执行,相当于在finally里面执行;可以通过配置throwing来获得拦截到的异常信息
aop:around(环绕增强):最强大的一种增强类型。 环绕增强可以在方法调用前后完成自定义的行为,环绕通知有两个要求,
1,方法必须要返回一个Object(返回的结果)
2,方法的第一个参数必须是ProceedingJoinPoint(可以继续向下传递的切入点)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 开始DI注解解析程序-->
<context:annotation-config/>
<!--<context:component-scan base-package="gzcPro.spring._09._staticproxy"></context:component-scan>-->
<!--<aop:aspectj-autoproxy/>-->
<!-- 事务管理对象-->
<bean id="txManager" class="gzcPro.spring._09._staticproxy.manager.TransactionManager"></bean>
<!-- 需要增强的真实类-->
<bean id="employeeService" class="gzcPro.spring._09._staticproxy.service.EmployeeServiceImpl"></bean>
<aop:config>
<aop:aspect ref="txManager">
<aop:pointcut id="px" expression="execution(* gzcPro.spring._09._staticproxy.service.*Service.*(..))"/>
<!--之前-->
<aop:before method="begin" pointcut-ref="px"></aop:before>
<!--没异常-->
<aop:after-returning method="commit" pointcut-ref="px"></aop:after-returning>
<!--有异常-->
<aop:after-throwing method="rollback" pointcut-ref="px" throwing="ex"></aop:after-throwing>
<!--finally-->
<aop:after method="close" pointcut-ref="px"></aop:after>
<!--环绕增强 将所有的增强合并-->
<!--<aop:around method="allInOne" pointcut-ref="px"></aop:around>-->
</aop:aspect>
</aop:config>
</beans>
或者
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 开始DI注解解析程序-->
<context:annotation-config/>
<!--<context:component-scan base-package="gzcPro.spring._09._staticproxy"></context:component-scan>-->
<!--<aop:aspectj-autoproxy/>-->
<!-- 事务管理对象-->
<bean id="txManager" class="gzcPro.spring._09._staticproxy.manager.TransactionManager"></bean>
<!-- 需要增强的真实类-->
<bean id="employeeService" class="gzcPro.spring._09._staticproxy.service.EmployeeServiceImpl"></bean>
<aop:config>
<aop:aspect ref="txManager">
<aop:pointcut id="px" expression="execution(* gzcPro.spring._09._staticproxy.service.*Service.*(..))"/>
<!--环绕增强 将所有的增强合并-->
<aop:around method="allInOne" pointcut-ref="px"></aop:around>
</aop:aspect>
</aop:config>
</beans>
或者
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 开始DI注解解析程序-->
<context:annotation-config/>
<!--@Component-->
<context:component-scan base-package="gzcPro.spring._09._staticproxy"></context:component-scan>
<!--@Aspect-->
<aop:aspectj-autoproxy/>
</beans>
@Autowired
private IEmployeeService service;
@Test
public void testSave() throws Exception {
System.out.println(service.getClass());
service.save();
}
@Test
public void testUpdate() throws Exception {
service.update();
}
class com.sun.proxy.$Proxy16
开启一个事务
回滚一个事务/ by zero
关闭一个事务
开启一个事务
更新一个员工
提交一个事务