一、Spring 概述
Spring框架是一个轻量级的企业级开发的一站式解决方案。所谓的解决方案就是可以基于Spring解决所有的Java EE开发的所有问题。
Spring框架主要提供了Ioc(Inversion of Control 控制反转)容器、AOP、数据访问、Web开发、消息、测试等相关技术的支持。
Spring使用简单的POJO(Plain Old Java Object 简单的普通Java对象)来进行企业级开发。每一个被Spring管理的Java的对象都被称为Bean;而Spring提供了一个Ioc容器来初始化对象,解决对象间的依赖管理和对象的使用。
(1)核心容器(Core Container)
Spring-Core:核心工具类,Spring其他模块大量使用Spring-Core;
Spring-Beans:Spring定义Bean的支持;
Spring-Context:运行时Spring容器;
Spring-Context-Support:Spring容器对第三方包的集成支持;
Spring-Expression:使用表达式语言在运行时查询和操作对象;
(2)AOP(Aspect Oriented Programming 面向切面编程)
Spring-AOP:基于代理的AOP支持;
Spring-Aspects:基于AspectJ的AOP支持;
(3)消息(Messaging)
Spring-Messaging:对消息架构和协议的支持。
(4)Web
Spring-Web:提供基础的Web集成功能,在Web项目中提供Spring的容器;
Spring-Webmvc:提供基于Servlet的Spring MVC;
Spring-Socket:提供WebSocket功能;
Spring-Webmvc-Portlet:提供Portlet环境支持;
(5)数据访问/集成(Data Access/Integration)
Spring-JDBC:提供以JDBC访问数据库的支持;
Spring-TX:提供编程式和声明式的事务支持;
Spring-ORM:提供对对象/关系映射技术的支持;
Spring-OXM:提供对对象/xml映射技术的支持;
Spring-JMS:提供对JMS的支持;
二、Spring项目搭建
1.使用IDEA创建Maven项目
File -> New -> Project -> Maven
填写自己的GroupId和ArtifactId后下一步,项目名默认就好,然后创建完成
创建后目录结构如下
修改pom.xml如下:
<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.ecworking</groupId> <artifactId>spring-study</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>spring-study</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <junit.version>3.8.1</junit.version> <java.version>1.8</java.version> <spring-context.version>4.1.6.RELEASE</spring-context.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring-context.version}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>${java.version}</source> <!-- 源代码使用的开发版本 --> <target>${java.version}</target> <!-- 需要生成的目标class文件的编译版本 --> <!-- 一般而言,target与source是保持一致的,但是,有时候为了让程序能在其他版本的jdk中运行(对于低版本目标jdk,源代码中需要没有使用低版本jdk中不支持的语法),会存在target不同于source的情况 --> </configuration> </plugin> </plugins> </build> </project>
修改完后会自动导入Maven依赖的jar包,依赖树如下:
三、Spring基础配置
Spring框架有四大基本原则:
1.使用POJO进行轻量级和最小侵入式开发;
2.通过依赖注入和基于接口编程实现松耦合;
3.通过AOP和默认习惯进行声明式编程;
4.使用AOP和模板减少模式化代码;
Spring所有的功能设计和实现都是基于此四大原则的。
3.1 依赖注入
我们常说的控制反转IOC和依赖注入DI在Spring环境下是等同的概念,控制反转通过依赖注入实现的。所谓的依赖注入指的是容器负责创建对象和维护对象间的依赖关系,而不是通过对象本身负责自己的创建和解决自己的依赖。
依赖注入的主要目的是为了解耦,体现了一种“组合”的概念。如果你希望你的类具备某项功能的时候,是继承一个此功能的父类好呢?还是组合一个此功能的类好?答案是不言而喻的,继承一个父类,子类将与父类耦合,而组合另一个类则使耦合度大大降低。
Spring Ioc容器(ApplicationgContext)负责创建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:JRS-330提供的注解。
- @Resource:JRS-250提供的注解。
@Autowired、@Inject、@Resource可注解在set方法或者属性上,一般习惯注解在属性上,优点是代码更少、层次更清晰。
接下来演示基于注解Bean的初始化和依赖注入,Spring容器类选用AnnotationConfigApplicationContext。
(1)编写功能类的Bean。
使用@Service注解声明当前FunctionService类是Spring管理的一个Bean。其中,使用@Component、@Service、@Repository和@Controller是等效的,可根据需要选用。
package com.ecworking.service; import org.springframework.stereotype.Service; @Service //使用@Service注解声明当前FunctionService类是Spring管理的一个Bean。其中,使用@Component、@Service、@Repository和@Controller是等效的,可根据需要选用。 public class FunctionService { public String sayHello(String name){ return name + " Hello!"; } }
(2)使用功能类Bean。
package com.ecworking.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service //使用@Service注解声明当前UseFunctionService类是Spring管理的一个Bean; public class UseFunctionService { @Autowired //使用@Autowired将FunctionService的实体Bean注入到UseFunctionService中,让UseFunctionService具备FunctionService的功能,此处使用@Inject注解或者@Resource注解是等效的。 FunctionService functionService; public String sayHello(String name){ return functionService.sayHello(name); } }
(3)配置类。
package com.ecworking; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration // 使用@Configuration声明当前类是一个配置类; @ComponentScan("com.ecworking.service") //使用@ComponentScan,自动扫描包名下所有使用@Service、@Component、@Repository和@Controller的类,并注册为Bean。 public class Config { }
(4)运行。
package com.ecworking; import com.ecworking.service.UseFunctionService; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class App { public static void main( String[] args ){ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class); //使用AnnotationConfigApplicationContext作为Spring容器,接受输入一个配置类作为参数; UseFunctionService useFuncService = context.getBean(UseFunctionService.class); //获得声明配置的UseFunctionService的Bean。 System.out.println(useFuncService.sayHello("DYP")); context.close(); } }
运行结果:
3.2 Java配置
Java配置是Spring4.x推荐的配置方式,可以完全代替xml配置;Java配置也是Spring Boot推荐的配置方式。
Java配置通过@Configuration和@Bean来实现的。
- @Configuration声明当前类是一个配置类,相当于一个Spring配置的xml文件。
- @Bean注解在方法上,声明当前方法返回值为一个Bean。
何时使用Java配置或者注解配置呢?我们主要的原则是:全局配置使用Java配置(如数据库相关配置、MVC相关配置),业务Bean的配置使用注解配置(@Component、@Service、@Repository、@Controller)。
(1)编写功能类的Bean。
package com.ecworking.service; //此处没有使用@Service声明Bean public class FunctionService { public String sayHello(String name){ return name + " Hello!"; } }
(2)使用功能类Bean。
package com.ecworking.service; //此处没有使用@Service声明Bean public class UseFunctionService { // 此处没有@Autowried注解注入Bean FunctionService functionService; public void setFunctionService(FunctionService functionService) { this.functionService = functionService; } public String sayHello(String name){ return functionService.sayHello(name); } }
(3)配置类。
package com.ecworking; import com.ecworking.service.FunctionService; import com.ecworking.service.UseFunctionService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
@Configuration // 声明当前类只一个配置类,此处没有使用包扫描,是因为所有的Bean都在此类中定义 public class Config { @Bean // 声明当前方法的返回值是一个Bean,Bean的名称是方法名 public FunctionService functionService(){ return new FunctionService(); } @Bean public UseFunctionService useFunctionService(){ UseFunctionService useFunctionService = new UseFunctionService(); useFunctionService.setFunctionService(functionService()); // 注入FunctionService的Bean的时候直接调用functionService() return useFunctionService; } }
(4)运行。
package com.ecworking; import com.ecworking.service.UseFunctionService; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class App { public static void main( String[] args ){ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class); UseFunctionService useFuncService = context.getBean(UseFunctionService.class); System.out.println(useFuncService.sayHello("DYP")); context.close(); } }
运行结果:
3.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依赖
<!-- spring-aop 支持 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.1.6.RELEASE</version> </dependency> <!-- aspectj 支持 --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.8.5</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.5</version> </dependency>
(2) 编写拦截规则的注解。
package com.ecworking.aop; import java.lang.annotation.*; // 这里讲下注解,注解本身是没有功能的,和xml一样。注解和xml都是一种元数据,元数据即解释数据的数据,这就是所谓的配置。 // 注解的功能来自用这个注解的地方 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Action { String name(); }
(3)编写使用注解的被拦截类。
package com.ecworking.aop; import org.springframework.stereotype.Service; @Service public class DemoAnnotationService { @Action(name = "注解式拦截的add操作") public void add(){} }
(4)编写使用方法规则被拦截类。
package com.ecworking.aop; import org.springframework.stereotype.Service; @Service public class DemoMethodService { public void add(){} }
(5)编写切面。
package com.ecworking.aop; import org.aspectj.lang.JoinPoint; 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; @Aspect // 通过 @Aspect 注解声明一个切面。 @Component // 通过 @Component 让此切面成为Spring管理的Bean。 public class LogAspect { @Pointcut("@annotation(com.ecworking.aop.Action)") // 通过 PonintCut 注解声明切点。 public void annotationPointCut(){ System.out.println("自动注入的切点"); } @After("annotationPointCut()") // 通过 @After 注解声明一个建言,并使用 @PointCut 定义的切点。 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()); // 通过反射可获得注解上的属性,然后做日志记录相关操作,下面的相同。 } /** * 定义切入点表达式 execution (* com.sample.service.impl..*.*(..)) * execution()是最常用的切点函数,其语法如下所示: * 整个表达式可以分为五个部分: * 1、execution(): 表达式主体。 * 2、第一个*号:表示返回类型,*号表示所有的类型。后面要加空格和包名隔开。 * 3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。 * 4、第二个*号:表示类名,*号表示所有的类。 * 5、*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数 */ @Before("execution(* com.ecworking.aop.DemoMethodService.*(..))") // 通过 @Before 注解声明一个建言,此建言直接使用拦截规则作为参数。 public void before(JoinPoint joinPoint){ MethodSignature signature = (MethodSignature)joinPoint.getSignature(); Method method = signature.getMethod(); System.out.println("方法式拦截:" + method.getName()); } }
(6)配置类。
package com.ecworking.aop; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @ComponentScan("com.ecworking.aop") @EnableAspectJAutoProxy // 使用@EnableAspectJAutoProxy 注解开启Spring对AspectJ代理的支持。 public class AopConfig { }
(7)运行。
package com.ecworking.aop; import org.springframework.context.annotation.AnnotationConfigApplicationContext; 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(); } }
运行结果: