1 前言
- Spring是一个轻量级开源框架,它是为了解决企业应用开发的复杂性而创立的。框架的次要劣势之一就是其分层架构,分层架构容许使用者抉择应用哪一个组件,同时为 J2EE 利用程序开发提供集成的框架。
- Spring是泛滥优良设计模式的组合(工厂、单例、代理、适配器、包装器、观察者、模板、策略)。
- Spring的用处不仅限于服务器端的开发。从简略性、可测试性和松耦合的角度而言,任何Java利用都能够从Spring中受害。
- Spring并未代替现有框架产品,而是将泛滥框架进行有机整合,简化企业级开发,俗称"胶水框架"。
2 Spring架构组成
Spring架构由诸多模块组成,可分类为
- 核心技术:依赖注入,事件,资源,i18n,验证,数据绑定,类型转换,SpEL,AOP。
- 测试:模仿对象,TestContext框架,Spring MVC测试,WebTestClient。
- 数据拜访:事务,DAO反对,JDBC,ORM,封送XML。
- Spring MVC和 Spring WebFlux Web框架。
- 集成:近程解决,JMS,JCA,JMX,电子邮件,工作,调度,缓存。
- 语言:Kotlin,Groovy,动静语言。
- List item
Spring架构组成如下图
3 Spring环境搭建
3.1 pom.xml中引入Spring罕用依赖
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation= "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.qf</groupId> <artifactId>hello-spring</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!-- Spring罕用依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.6.RELEASE</version> </dependency> </dependencies></project>
3.2 创立Spring配置文件
命名无限度,约定俗成命名有:spring-context.xml、applicationContext.xml、beans.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"></beans>
4 Spring工厂编码(入门程序)
4.1 定义指标Bean类型
public class MyClass{ public void show(){ System.out.println("HelloWorld"); }}
4.2spring-context.xml中的< beans >外部配置bean
在spring-context.xml中配置MyClass的bean后,当我的项目启动时,spring容器会主动创立MyClass实例,这个实例名字叫mc
<!-- 配置实例(id:“惟一标识” class="须要被创立的指标对象全限定名") --><bean id="mc" class="com.qf.spring.part1.factory.MyClass" />
测试代码
public class TestFactory{ /** * 程序中的对象都交由Spring的ApplicationContext工厂进行创立。 */ public static void main(String[] args){ //1\. 读取配置文件中所需创立的bean对象,并取得工厂对象 ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-context.xml"); //2\. 通过id获取bean对象 MyClass mc = (MyClass) ctx.getBean("mc"); //3\. 应用对象 mc.show(); }}
5 IoC(Inversion of Control )管制反转
管制反转是Spring框架的外围,所谓管制反转就是利用自身不负责依赖对象的创立及保护,依赖对象的创立及保护是由内部容器负责的, 这样控制权就由利用转移到了内部容器,控制权的转移就是所谓反转。这样就由之前的本人创立依赖对象,变为由spring容器创立。(变被动为被动,即反转)。管制反转解决了具备依赖关系的组件之间的强耦合,使得我的项目状态更加持重。
5.1 我的项目中强耦合问题
public class UserDAOImpl implements UserDAO{....}
public class UserServiceImpl implements UserService { // 通过传统的new形式强耦合了UserDAOImpl!!!,使得UserServiceImpl变得不持重!! private UserDAO userDAO= new UserDAOImpl(); @Override public User queryUser() { return userDAO.queryUser(); } ....}
5.2 解决方案
// 不援用任何一个具体的组件(实现类),在须要其余组件的地位预留存取值入口(set/get)public class UserServiceImpl implements UserService { // !!!不再耦合任何DAO实现!!!,打消不持重因素!! private UserDAO userDAO; // 为userDAO定义set/get,容许userDAO属性接管spring赋值 //Getters And Setters @Override public User queryUser() { return userDAO.queryUser(); } ....}
在spring配置文件中配置UserDAO和UserService对应的bean
<bean id="userDAO" class="com.qf.spring.part1.injection.UserDaoImpl"></bean><!-- UserServiceImpl组件 --><bean id="userService" class="com.qf.spring.part1.injection.UserServiceImpl"> <!-- 由spring为userDAO属性赋值,值为id="userDAO"的bean --> <property name="userDAO" ref="userDAO"/></bean>
6 DI(Dependency Injection)依赖注入
6.1 概念
在Spring创建对象的同时,为其属性赋值,称之为依赖注入,注入形式次要有以下2种
- 构造函数注入
- Setter办法注入
6.2 Setter办法注入
创建对象时,Spring工厂会通过Setter办法为对象的属性赋值。
6.2.1 定义指标Bean类型
public class User { private Integer id; private String password; private String sex; private Integer age; private Date bornDate; private String[] hobbys; private Set<String> phones; private List<String> names; private Map<String,String> countries; private Properties files; //Getters And Setters}
6.2.2 根本类型 + 字符串类型 + 日期类型
<bean id="u1" class="com.qf.spring.part1.injection.User"> <!--base field--> <property name="id" value="1001" /> <property name="password" value="123456" /> <property name="sex" value="male" /> <property name="age" value="20" /> <property name="bornDate" value="1990/1/1" /><!--留神格局"/"--></bean>
6.2.3 容器类型(list,set,map,Properties)
<bean id="u1" class="com.qf.spring.part1.injection.User"> <!--Array--> <property name="hobbys"> <array> <value>Run</value> <value>Swim</value> <value>Climb</value> </array> </property> <!--Set--> <property name="phones"> <set> <value>13777777777</value> <value>13888888888</value> <value>13999999999</value> </set> </property> <!--List--> <property name="names"> <list> <value>tom</value> <value>jack</value> <value>marry</value> </list> </property> <!--Map--> <property name="countries"> <map> <entry key="CN" value="China" /> <entry key="US" value="America" /> <entry key="KR" value="Korea" /> </map> </property> <!--Properties--> <property name="files"> <props> <prop key="first">One</prop> <prop key="second">Two</prop> <prop key="third">Three</prop> </props> </property></bean>
6.2.4 自定义类型
<!--主要bean,被作为属性--><bean id="addr" class="com.qf.spring.part1.injection.Address"> <property name="position" value="北京市海淀区" /> <property name="zipCode" value="100001" /></bean><!--次要bean,操作的主体--><bean id="u2" class="com.qf.spring.part1.injection.User"> <property name="address" ref="addr" /><!--address属性援用addr对象--></bean>
6.3 结构注入
创建对象时,Spring工厂会通过构造方法为对象的属性赋值。
6.3.1 定义指标Bean类型
public class Student { private Integer id; private String name; private String sex; private Integer age; //Constructors public Student(Integer id , String name , String sex , Integer age){ this.id = id; this.name = name; this.sex = sex; this.age = age; }}
6.3.2 注入
<!--结构注入--><bean id="u3" class="com.qf.zcg.spring.day1.t2.ioc.Student"> <constructor-arg name="id" value="1234" /> <!-- 除标签名称有变动,其余均和Set注入统一 --> <constructor-arg name="name" value="tom" /> <constructor-arg name="age" value="20" /> <constructor-arg name="sex" value="male" /></bean>
7 Spring工厂个性
7.1 饿汉式创立劣势
工厂创立之后,会将Spring配置文件中的所有对象都创立实现(饿汉式),进步程序运行效率,防止屡次IO,缩小对象创立工夫。(概念靠近连接池,一次性创立好,应用时间接获取)
7.2 生命周期办法
- 自定义初始化办法:增加“init-method”属性,Spring则会在创建对象之后,调用此办法。
- 自定义销毁办法:增加“destroy-method”属性,Spring则会在销毁对象之前,调用此办法。
- 销毁:工厂的close()办法被调用之后,Spring会毁掉所有已创立的单例对象。
- 分类:Singleton对象由Spring容器销毁、Prototype对象由JVM销毁。
7.3 生命周期注解
import javax.annotation.PostConstruct;import javax.annotation.PreDestroy;@PostConstruct //初始化 public void init(){ System.out.println("init method executed");}@PreDestroy //销毁public void destroy(){ System.out.println("destroy method executed");}
7.4 生命周期阶段
单例bean:singleton
随工厂启动创立 ==》 构造方法 ==》 set办法(注入值) ==》 init(初始化) ==》 构建实现 ==》随工厂敞开销毁
多例bean:prototype
被应用时创立 ==》 构造方法 ==》 set办法(注入值) ==》 init(初始化) ==》 构建实现 ==》JVM垃圾回收销毁
8 代理设计模式
8.1 概念
将外围性能与辅助性能(事务、日志、性能监控代码)拆散,达到外围业务性能更纯正、辅助业务性能可复用。
8.2 动态代理设计模式
通过代理类的对象,为原始类的对象(指标类的对象)增加辅助性能,更容易更换代理实现类、利于保护。
- 代理类 = 实现原始类雷同接口 + 增加辅助性能 + 调用原始类的业务办法。
动态代理的问题
- 代理类数量过多,不利于我的项目的治理。
- 多个代理类的辅助性能代码冗余,批改时,维护性差。
8.3 动静代理设计模式
8.3.1 JDK动静代理实现(基于接口)
//指标final OrderService os = new OrderServiceImpl();//额定性能InvocationHandler handler = new InvocationHandler(){//1.设置回调函数(额定性能代码) @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("start..."); method.invoke(os, args); System.out.println("end..."); return null; }};//2.创立动静代理类Object proxyObj = Proxy.newProxyInstance(ClassLoader , Interfaces , InvocationHandler);
8.3.2 CGlib动静代理实现(基于继承)
final OrderService os = new OrderServiceImpl();Enhancer cnh = new Enhancer();//1.创立字节码曾强对象enh.setSuperclass(os.getClass());//2.设置父类(等价于实现原始类接口)enh.setCallback(new InvocationHandler(){//3.设置回调函数(额定性能代码) @Override public Object invoke(Object proxy , Method method, Object[] args) throws Throwable{ System.out.println("start..."); Object ret = method.invoke(os,args); System.out.println("end..."); return ret; }});OrderService proxy = (OrderService)enh.create();//4.创立动静代理类proxy,createOrder();
9 面向切面编程
9.1 概念
AOP(Aspect Oriented Programming),即面向切面编程,利用一种称为"横切"的技术,剖开封装的对象外部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简略说就是那些与业务无关,却为业务模块所独特调用的逻辑或责任封装起来,便于缩小零碎的反复代码,升高模块之间的耦合度,并有利于将来的可操作性和可维护性。
艰深的概念来讲,所谓的面向切面编程就是针对被代理对象的办法在某个特定的执行机会(办法调用之前、办法调用之后、办法抛出异样),做出一些额定的横向的解决。益处在于:1. 能够在不批改原有代码根底上横向扩大咱们的内容;2. 将一些办法中的通用逻辑进行统一化的解决。
OOP(面向对象编程)和AOP(面向切面编程)的区别:oop是对类纵向的扩大;aop是横向的扩大。
9.2 AOP开发术语
- 连接点(Joinpoint):连接点是程序类中客观存在的办法,可被Spring拦挡并切入内容。
- 切入点(Pointcut):被切入连接点。
- 告诉、加强(Advice):能够为切入点增加额定性能,分为:前置告诉、后置告诉、异样告诉、盘绕告诉等。
- 指标对象(Target):代理的指标对象
- 织入(Weaving):把告诉利用到具体的类,进而创立新的代理类的过程。
- 代理(Proxy):被AOP织入告诉后,产生的后果类。
- 切面(Aspect):由切点和告诉组成,将横切逻辑织入切面所指定的连接点中。
9.3 作用
Spring的AOP编程即是通过动静代理类为原始类的办法增加辅助性能。
9.4 环境搭建
引入AOP相干依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.1.6.RELEASE</version></dependency>
spring-context.xml引入AOP命名空间
<?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/aop http://www.springframework.org/schema/aop/spring-aop.xsd "></beans>
9.5 开发流程
定义原始类
package com.qf.aaron.aop.basic;public interface UserService { public void save();}
package com.qf.aaron.aop.basic;public class UserServiceImpl implements UserService { public void save() { System.out.println("save method executed..."); }}
定义告诉类(增加额定性能
package com.qf.aaron.aop.basic;import org.springframework.aop.MethodBeforeAdvice;import java.lang.reflect.Method;public class MyAdvice implements MethodBeforeAdvice { //实现前置告诉接口 public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("before advice executed..."); }}
定义bean标签
<!--原始对象--><bean id="us" class="com.qf.aaron.aop.basic.UserServiceImpl" /><!--辅助对象--><bean id="myAdvice" class="com.qf.aaron.aop.basic.MyAdvice" />
定义切入点(PointCut)
造成切面(Aspect)
<aop:config> <!--切点--> <aop:pointcut id="myPointCut" expression="execution(* save())" /></aop:config>
<aop:config>
<!--组装切面 --><aop:advisor advice-ref="myAdvice" pointcut-ref="myPointCut" />
</aop:config>
### 9.6 告诉类可定义的告诉类有6种,能够按需要抉择告诉类。
前置告诉:MethodBeforeAdvice
后置告诉:AfterAdvice
后置告诉:AfterReturningAdvice //有异样不执行,办法会因异样而完结,无返回值
异样告诉:ThrowsAdvice
盘绕告诉:MethodInterceptor
### 9.7 JDK动静代理和CGLIB动静代理的抉择* spring底层,蕴含了jdk代理和cglib代理两种动静代理生成机制* 根本规定是:指标业务类如果有接口则用JDK代理,没有接口则用CGLib代理然而spring中默认开启JDK动静代理,当须要应用CGLIB动静代理时,须要在spring配置文件中配置。
<!-- 应用cglib的形式实现aop -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
## 10 基于aspectJ的AOP实现编写aspectJ告诉代码:
@Aspect
public class AspectJAdvisor {
// 盘绕告诉@Around("execution(* org.example.service.impl.*.*(..))")public Object timer(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("前置告诉"); Object o = pjp.proceed(); System.out.println("【后置告诉】"); return o;}// 后置告诉@After("execution(* org.example.service.impl.*.*(..))")public void after(JoinPoint jp) throws Throwable{ System.out.println("====After method invokded====");}// 前置告诉@Before("execution(* org.example.service.impl.*.*(..))")public void before(JoinPoint jp){ System.out.println("====before method invoked====");}// 失常返回的告诉@AfterReturning("execution(* org.example.service.impl.*.*(..))")public void afterReturning(JoinPoint jp){ System.out.println("====after value return====");}// 抛出异样后的告诉,办法的异样必须与代理类抛出的异样统一,throwing的值要与异样的形参名保持一致@AfterThrowing(value = "execution(* org.example.service.impl.*.*(..))", throwing="npe")public void afterThrowException(JoinPoint jp, NullPointerException npe){ System.out.println("====after exception throwing====");}
}
配置
<bean id="userService" class="org.example.service.impl.UserServiceImpl"></bean>
<bean id="throwsAdvisor" class="org.example.advisor.AspectJAdvisor"></bean>
<!--- aspectJ是应用cglib来实现动静代理的 -->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
## 11 基于注解开发### 11.1 申明bean用于替换自建类型组件的 <bean…>标签;能够更疾速的申明bean* @Service 业务类专用 @Repository dao实现类专用 @Controller web层专用* @Component 通用* @Scope 用户管制bean的创立模式
// @Service阐明 此类是一个业务类,须要将此类纳入工厂 等价替换掉 <bean class="xxx.UserServiceImpl">
// @Service默认beanId == 首字母小写的类名"userServiceImpl"
// @Service("userService") 自定义beanId为"userService"
@Service //申明bean,且id="userServiceImpl"
@Scope("singleton") //申明创立模式,默认为单例模式 ;@Scope("prototype")即可设置为多例模式
public class UserServiceImpl implements UserService {
...
}
### 11.2 注入(DI)用于实现bean中属性值的注入* @Autowired 基于类型主动注入* @Resource 基于名称主动注入* @Qualifier(“userDAO”) 限定要主动注入的bean的id,个别和@Autowired联用* @Value 注入简略类型数据 (jdk8种+String)
@Service
public class UserServiceImpl implements UserService {
@Autowired //注入类型为UserDAO的bean@Qualifier("userDAO2") //如果有多个类型为UserDAO的bean,能够用此注解从中筛选一个private UserDAO userDAO;
}
@Service
public class UserServiceImpl implements UserService {
@Resource("userDAO3") //注入id=“userDAO3”的beanprivate UserDAO userDAO;/*@Resource //注入id=“userDAO”的beanprivate UserDAO userDAO;*/
}
public class XX{
@Value("100") //注入数字private Integer id;@Value("shine") //注入Stringprivate String name;
}
### 11.3 事务管制用于管制事务切入* @Transactional* 工厂配置中的 <tx:advice… 和 <aop:config… 能够省略 !!
//类中的每个办法都切入事务(有本人的事务管制的办法除外)
@Transactional(isolation=Isolation.READ_COMMITTED,propagation=Propagation.REQUIRED,readOnly=false,rollbackFor=Exception.class,timeout = -1)
public class UserServiceImpl implements UserService {
...//该办法本人的事务管制,仅对此办法无效@Transactional(propagation=Propagation.SUPPORTS)public List<User> queryAll() { return userDao.queryAll();}public void save(User user){ userDao.save(user);}
}
### 11.4 注解所需配置
<!-- 告知spring,哪些包中 有被注解的类、办法、属性 -->
<!-- <context:component-scan base-package="com.qf.a,com.xx.b"></context:component-scan> -->
<context:component-scan base-package="com.qf"></context:component-scan>
<!-- 告知spring,@Transactional在定制事务时,基于txManager=DataSourceTransactionManager -->
<tx:annotation-driven transaction-manager="txManager"/>
### 11.5 AOP开发#### 11.5.1 注解应用
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect // 申明此类是一个切面类:会蕴含切入点(pointcut)和告诉(advice)
@Component //申明组件,进入工厂
public class MyAspect {
// 定义切入点@Pointcut("execution(* com.qf.spring.service.UserServiceImpl.*(..))")public void pc(){}@Before("pc()") // 前置告诉public void mybefore(JoinPoint a) { System.out.println("target:"+a.getTarget()); System.out.println("args:"+a.getArgs()); System.out.println("method's name:"+a.getSignature().getName()); System.out.println("before~~~~");}@AfterReturning(value="pc()",returning="ret") // 后置告诉public void myAfterReturning(JoinPoint a,Object ret){ System.out.println("after~~~~:"+ret);}@Around("pc()") // 盘绕告诉public Object myInterceptor(ProceedingJoinPoint p) throws Throwable { System.out.println("interceptor1~~~~"); Object ret = p.proceed(); System.out.println("interceptor2~~~~"); return ret;}@AfterThrowing(value="pc()",throwing="ex") // 异样告诉public void myThrows(JoinPoint jp,Exception ex){ System.out.println("throws"); System.out.println("===="+ex.getMessage());}
}
#### 11.5.2 配置
<!-- 增加如下配置,启用aop注解 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
## 12 Spring单元测试### 12.1 导入依赖
<dependency>
<groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version>
</dependency>
### 12.2 测试编码能够免去工厂的创立过程;能够间接将要测试的组件注入到测试类。
@RunWith(SpringJUnit4ClassRunner.class) //由SpringJUnit4ClassRunner启动测试
@ContextConfiguration("classpath:applicationContext.xml") //spring的配置文件地位
public class SpringTest{//以后测试类也会被纳入工厂中,所以其中属性能够注入
@Autowired // 注入要测试的组件@Qualifier("userDAO")private UserDAO userDAO;@Testpublic void test(){ // 测试应用userDAO userDAO.queryUser(); ....}
}
## 最初感激你看到这里,看完有什么的不懂的能够在评论区问我,感觉文章对你有帮忙的话记得给我点个赞,每天都会分享java相干技术文章或行业资讯,欢送大家关注和转发文章!