目录
1.spring概述
Spring框架是一个轻量级的企业级开发的一站式解决方案。所谓解决方案就是可以基于Spring解决Java EE开发的所有问题。Spring框架主要提供了IoC容器、AOP、数据访问、Web开发、消息、测试等相关技术的支持。
Spring使用简单的POJO(Plain Old Java Object,即无任何限制的普通Java对象)来进行企业级开发。
每一个被Spring管理的Java对象都称之为Bean;而Spring提供了一个IoC容器用来初始化对象,解决对象间的依赖管理和对象的使用。
1.1.spring模块
(1)核心容器(Core Container)
Spring-Core:核心工具类,Spring其他模块大量使用Spring-Core;
Spring-Beans:Spring定义Bean的支持;
Spring-Context:运行时Spring容器;
Spring-Context-Support:Spring容器对第三方包的集成支持;
Spring-Expression:使用表达式语言在运行时查询和操作对象。
(2)AOP
Spring-AOP:基于代理的AOP支持;
Spring-Aspects:基于AspectJ的AOP支持。
(3)消息(Messaging)
Spring-Messaging:对消息架构和协议的支持。
(4)Web
Spring-Web:提供基础的Web集成的功能,在Web项目中提供Spring的容器;
Spring-Webmvc:提供基于Servlet的Spring MVC;
Spring-WebSocket:提供WebSocket功能;
Spring-Webmvc-Portlet:提供Portlet环境支持。
(5)数据访问/集成(Data Access/Integration)
Spring-JDBC:提供以JDBC访问数据库的支持;
Spring-TX:提供编程式和声明式的事务支持;
Spring-ORM:提供对对象/关系映射技术的支持;
Spring-OXM:提供对对象/xml映射技术的支持;
Spring-JMS:提供对JMS的支持。
1.2 spring的生态
Spring发展到现在已经不仅仅是Spring框架本身的内容,Spring目前提供了大量的基于Spring的项目,可以用来更深入地降低我们的开发难度,提高开发效率。
目前Spring的生态里主要有以下项目,我们可以根据自己项目的需要来选择使用相应的项目。
Spring Boot:使用默认开发配置来实现快速开发。
Spring XD:用来简化大数据应用开发。
Spring Cloud:为分布式系统开发提供工具集。
Spring Data:对主流的关系型和NoSQL数据库的支持。
Spring Integration:通过消息机制对企业集成模式(EIP)的支持。
Spring Batch:简化及优化大量数据的批处理操作。
Spring Security:通过认证和授权保护应用。
Spring HATEOAS:基于HATEOAS原则简化REST服务开发。
Spring Social:与社交网络API(如Facebook、新浪微博等)的集成。
Spring AMQP:对基于AMQP的消息的支持。
Spring Mobile:提供对手机设备检测的功能,给不同的设备返回不同的页面的支持。
Spring for Android:主要提供在Android上消费RESTful API的功能。
Spring Web Flow:基于Spring MVC提供基于向导流程式的Web应用开发。
Spring Web Services:提供了基于协议有限的SOAP/Web服务。
Spring LDAP:简化使用LDAP开发。
Spring Session:提供一个API及实现来管理用户会话信息。
2.Spring基础配置
Spring框架本身有四大原则:
1)使用POJO进行轻量级和最小侵入式开发。
2)通过依赖注入和基于接口编程实现松耦合。
3)通过AOP和默认习惯进行声明式编程。
4)使用AOP和模板(template)减少模式化代码。
Spring所有功能的设计和实现都是基于此四大原则的。
2.1 依赖注入
我们经常说的控制翻转(Inversion of Control-IOC)和依赖注入(dependency injection-DI)在Spring环境下是等同的概念,控制翻转是通过依赖注入实现的。所谓依赖注入指的是容器负责创建对象和维护对象间的依赖关系,而不是通过对象本身负责自己的创建和解决自己的依赖。
依赖注入的主要目的是为了解耦,体现了一种“组合”的理念。
Spring IoC容器(ApplicationContext)负责创建Bean,并通过容器将功能类Bean注入到你需要的Bean中。Spring提供使用xml、注解、Java配置、groovy配置实现Bean的创建和注入。
无论是xml配置、注解配置还是Java配置,都被称为配置元数据,所谓元数据即描述数据的数据。元数据本身不具备任何可执行的能力,只能通过外界代码来对这些元数据行解析后进行一些有意义操作。Spring容器解析这些配置元数据进行Bean初始化、配置和管理依赖。
声明Bean的注解:
@Component组件,没有明确的角色。
@Service在业务逻辑层(service层)使用。
@Repository在数据访问层(dao层)使用。
@Controller在展现层(MVC→Spring MVC)使用。
注入Bean的注解,一般情况下通用。
@Autowired:Spring提供的注解。
@Inject:JSR-330提供的注解。
@Resource:JSR-250提供的注解。
@Autowired、@Inject、@Resource可注解在set方法上或者属性上。
代码示例
1.编写功能类的bean
package com.springbootdemo.study;
import org.springframework.stereotype.Service;
/**
* FunctionService
*
* @Description
*/
//使用@Service注解声明当前FunctionService类是Spring管理的一个Bean
@Service
public class FunctionService {
public String sayHello(String word){
return "Hello " + word +" !";
}
}
2.使用功能类的bean
package com.springbootdemo.study;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* UseFunctionService
*
* @Description
*/
// 使用@Service注解声明当前UseFunctionService类是Spring管理的一个Bean。
@Service
public class UseFunctionService {
/**
* 使用@Autowired将FunctionService的实体Bean注入到UseFunctionService中,
* 让UseFunctionService具备FunctionService的功能,此处使用JSR-330的@Inject注解或者JSR-250的@Resource注解是等效的。
*/
@Autowired
FunctionService functionService;
public String sayHello(String word){
return functionService.sayHello(word);
}
}
3.配置类
package com.springbootdemo.study;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* DiConfig
*
* @Description
*/
//@Configuration声明当前类是一个配置类
@Configuration
//使用@ComponentScan,自动扫描包名下所有使用@Service、@Component、@Repository和@Controller的类,并注册为Bean。
@ComponentScan("com.springbootdemo.study")
public class DiConfig {
}
4.运行测试
package com.springbootdemo.study;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* Main
* @Description
*/
public class Main {
public static void main(String[] args) {
//使用AnnotationConfigApplicationContext作为Spring容器,接受输入一个配置类作为参数;
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DiConfig.class);
//获得声明配置的UseFunctionService的Bean。
UseFunctionService useFunctionService = context.getBean(UseFunctionService.class);
System.out.println(useFunctionService.sayHello("di"));
context.close();
}
}
2.2 java配置
Java配置是Spring 4.x推荐的配置方式,可以完全替代xml配置;Java配置也是Spring Boot推荐的配置方式。Java配置是通过@Configuration和@Bean来实现的。
@Configuration声明当前类是一个配置类,相当于一个Spring配置的xml文件。
@Bean注解在方法上,声明当前方法的返回值为一个Bean。
开发时一般使用Java配置和注解混合配置。何时使用Java配置或者注解配置呢?主要原则是:全局配置使用Java配置(如数据库相关配置、MVC相关配置),业务Bean的配置使用注解配置(@Service、@Component、@Repository、@Controller)。
代码示例
1.编写功能类的bean
package com.springbootdemo.study.javaconfig;
/**
* FunctionService
*
* @Description
*/
public class FunctionService {
public String sayHello(String word){
return "Hello " + word +" !";
}
}
2.使用功能类的bean
package com.springbootdemo.study.javaconfig;
/**
* UseFunctionService
* @Description
*/
public class UseFunctionService {
FunctionService functionService;
public void setFunctionService(FunctionService functionService) {
this.functionService = functionService;
}
public String sayHello(String word){
return functionService.sayHello(word);
}
}
3.配置类
package com.springbootdemo.study.javaconfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* JavaConfig
*
* @Description
*/
//使用@Configuration注解表明当前类是一个配置类,这意味着这个类里可能有0个或者多个@Bean注解,此处没有使用包扫描,是因为所有的Bean都在此类中定义了。
@Configuration
public class JavaConfig {
//使用@Bean注解声明当前方法FunctionService的返回值是一个Bean,Bean的名称是方法名。
@Bean
public FunctionService functionService(){
return new FunctionService();
}
@Bean
public UseFunctionService useFunctionService(){
UseFunctionService useFunctionService = new UseFunctionService();
//注入FunctionService的Bean时候直接调用functionService()。
useFunctionService.setFunctionService(functionService());
return useFunctionService;
}
/**
* 另外一种注入的方式,直接将FunctionService作为参数给useFunctionService(),这也是Spring容器提供的极好的功能。
* 在Spring容器中,只要容器中存在某个Bean,就可以在另外一个Bean的声明方法的参数中写入。
* @param functionService
* @return
*/
@Bean
public UseFunctionService useFunctionService(FunctionService functionService){
UseFunctionService useFunctionService = new UseFunctionService();
useFunctionService.setFunctionService(functionService);
return useFunctionService;
}
}
4.运行测试
package com.springbootdemo.study.javaconfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* Main
* @Description
*/
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class);
UseFunctionService useFunctionService = context.getBean(UseFunctionService.class);
System.out.println(useFunctionService.sayHello("java config"));
context.close();
}
}
2.3 AOP
AOP:面向切面编程,相对于OOP面向对象编程。
Spring的AOP的存在目的是为了解耦。AOP可以让一组类共享相同的行为。在OOP中只能通过继承类和实现接口,来使代码的耦合度增强,且类继承只能为单继承,阻碍更多行为添加到一组类上,AOP弥补了OOP的不足。
Spring支持AspectJ的注解式切面编程。
(1)使用@Aspect声明是一个切面。
(2)使用@After、@Before、@Around定义建言(advice),可直接将拦截规则(切点)作为参数。
(3)其中@After、@Before、@Around参数的拦截规则为切点(PointCut),为了使切点复用,可使用@PointCut专门定义拦截规则,然后在@After、@Before、@Around的参数中调用。
(4)其中符合条件的每一个被拦截处为连接点(JoinPoint)。
本文基于注解拦截和基于方法规则拦截两种方式,演示一种模拟记录操作的日志系统的实现。其中注解式拦截能够很好地控制要拦截的粒度和获得更丰富的信息,Spring本身在事务处理(@Transcational)和数据缓存(@Cacheable等)上面都使用此种形式的拦截。
代码示例
1.添加spring aop支持及AspectJ依赖
本人使用idea构建的springboot项目,需要引入以下jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.编写拦截规则的注解
package com.springbootdemo.study.aop;
import java.lang.annotation.*;
/**
* Action
*
* 这里讲下注解,注解本身是没有功能的,就和xml一样。注解和xml都是一种元数据,元数据即解释数据的数据,这就是所谓配置。
* 注解的功能来自用这个注解的地方。
* @Description
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Action {
String name();
}
3.编写使用注解的被拦截类
package com.springbootdemo.study.aop;
import org.springframework.stereotype.Service;
/**
* DemoAnnotationService
* 使用注解的被拦截类
* @Description
*/
@Service
public class DemoAnnotationService {
@Action(name = "注解式拦截的add操作")
public void add(){
}
}
4.编写使用方法规则被拦截类
package com.springbootdemo.study.aop;
import org.springframework.stereotype.Service;
/**
* DemoMethodService
* 使用方法规则被拦截类。
* @Description
*/
@Service
public class DemoMethodService {
public void add(){
}
}
5.编写切面
package com.springbootdemo.study.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* LogAspect
*
* @Description
*/
@Component //通过@Component让此切面成为Spring容器管理的Bean。
@Aspect //通过@Aspect注解声明一个切面。
public class LogAspect {
//通过@PointCut注解声明切点。
@Pointcut("@annotation(com.springbootdemo.study.aop.Action)")
public void annotationPointCut(){}
//通过@After注解声明一个建言,并使用@PointCut定义的切点。
@After("annotationPointCut()")
public void after(JoinPoint joinPoint){
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
Method method = signature.getMethod();
//通过反射可获得注解上的属性,然后做日志记录相关的操作,下面的相同。
Action action = method.getAnnotation(Action.class);
System.out.println("注解式拦截 " + action.name());
}
//通过@Before注解声明一个建言,此建言直接使用拦截规则作为参数。
@Before("execution(* com.springbootdemo.study.aop.DemoMethodService.*(..))")
public void before(JoinPoint joinPoint){
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
Method method = signature.getMethod();
System.out.println("方法规则式拦截," + method.getName());
}
}
6.配置类
package com.springbootdemo.study.aop;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* AopConfig
*
* @Description
*/
@Configuration
@ComponentScan("com.springbootdemo.study.aop")
@EnableAspectJAutoProxy //使用@EnableAspectJAutoProxy注解开启Spring对AspectJ代理的支持。
public class AopConfig {
}
7.运行测试
package com.springbootdemo.study.aop;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* Main
*
* @Description
*/
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class);
DemoAnnotationService demoAnnotationService = context.getBean(DemoAnnotationService.class);
DemoMethodService demoMethodService = context.getBean(DemoMethodService.class);
demoAnnotationService.add();
demoMethodService.add();
context.close();
}
}