读spring in action.
- 环境搭建
- quick-start依赖注入
- 面向切面
1.环境搭建
- jdk1.8
- gradle 2.12
- Intelij idea 2016.2.1
1.1创建一个gradle项目
在idea中,new -> project -> gradle 创建一个空项目。创建成功后修改build.gradle :
group 'com.test' version '1.0-SNAPSHOT' apply plugin: 'java' apply plugin: 'war' sourceCompatibility = 1.8 targetCompatibility = 1.8 repositories { mavenLocal() mavenCentral() } dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' }
根目录下创建.gitignore:
# Created by .ignore support plugin (hsz.mobi) .idea/ .gradle/ build/ out/ */build/
1.2 quick start
spring的核心是依赖注入,那么简单的做一个入门测试。
在项目名上右键,new->module->gradle->创建一个java项目quick-start. 修改生产的build.gradle:
group 'com.test' version '1.0-SNAPSHOT' apply plugin: 'java' sourceCompatibility = 1.8 repositories { mavenLocal() mavenCentral() } dependencies { testCompile group: 'junit', name: 'junit', version: '4.11' compile 'org.springframework:spring-context:4.3.2.RELEASE' }
这里参考:http://projects.spring.io/spring-framework/#quick-start 的案例.
在quick-start module下创建一个package src/main/java,在java文件夹上右键,Mark Directory as -> Sources Root.
这时候idea的项目配置还没有刷新,需要手动点击一下刷新:
这时候,idea就可以resolve quick-start这个项目以及他的dependency了。
添加spring-context会添加其他依赖:
dependencies { compile 'org.springframework:spring-context:4.3.2.RELEASE' }
1.2.1 Hello World
我们来创建打印消息的组件。MessagePrinter打印一个MessageService的实例的信息。:
创建接口com.test.hello.MessageService:
package com.test.hello; /** * Created by rmiao on 8/15/2016. */ public interface MessageService { String getMessage(); }
创建组件com.test.hello.MessagePrinter:
package com.test.hello; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * Created by rmiao on 8/15/2016. */ @Component public class MessagePrinter { final private MessageService service; @Autowired public MessagePrinter(MessageService service){ this.service = service; } public void printMessage(){ System.out.println(this.service.getMessage()); } }
@Component声明MessagePrinter是一个bean,由spring容器来管理。
@Autowired 这里是构造器注入,会根据构造器参数的类型和参数名来将spring容器中的bean注入构造器。
针对MessagePrinter的注入参数,我们需要一个MessageService的实现:
MessageService mockMessageService(){ return () -> "Hello World!"; }
下面开始启动spring容器来测试这个打印消息组件:
创建com.test.hello.Application:
package com.test.hello; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * Created by rmiao on 8/15/2016. */ @Configuration @ComponentScan public class Application { @Bean MessageService mockMessageService(){ return () -> "Hello World!"; } public static void main(String[] args){ ApplicationContext context = new AnnotationConfigApplicationContext(Application.class); MessagePrinter printer = context.getBean(MessagePrinter.class); printer.printMessage(); } }
@Configuration来声明Application是一个配置类,相当于xml配置文件。这里只配置了一个bean mockMessageService.
@Bean 用来声明一个bean并交由spring容器管理。相当于xml配置文件中<bean>. 这种方式表示声明一个MessageService的类的bean,bean id为mockMessageService。
@ComponentScan来声明spring容器扫描范围,这种方式表示扫描Application所在包以及子包下的所有类,然后将识别到的bean放到spring容器中。
AnnotationConfigApplicationContext用来创建spring容器。getBean来获取容器中的bean。
最终,打印Hello World!
1.2.2 Aop面向切面
spring的另一个强大特性是面向切面编程。可以在任意方法的调用前后进行一些操作。比如记录日志:
添加aop依赖:
dependencies { compile group: 'org.springframework', name: 'spring-aop', version: '4.3.2.RELEASE' compile group: 'org.aspectj', name: 'aspectjweaver', version: '1.8.9' }
添加logback日志组件依赖:
dependencies { compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.21' compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.1.7' compile group: 'ch.qos.logback', name: 'logback-core', version: '1.1.7' compile group: 'org.codehaus.groovy', name: 'groovy', version: '2.4.7' }
在src/main/resources下创建logback.groovy:
import ch.qos.logback.classic.encoder.PatternLayoutEncoder import ch.qos.logback.core.ConsoleAppender import ch.qos.logback.core.rolling.FixedWindowRollingPolicy import ch.qos.logback.core.rolling.RollingFileAppender import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy import static ch.qos.logback.classic.Level.DEBUG import static ch.qos.logback.classic.Level.INFO appender("STDOUT", ConsoleAppender) { encoder(PatternLayoutEncoder) { pattern = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n" } } appender("FILE", RollingFileAppender){ file = "quick-start/log/project.log" encoder(PatternLayoutEncoder) { pattern = "%d{yyyy-MM-dd_HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" } rollingPolicy(FixedWindowRollingPolicy) { fileNamePattern = "quick-start/log/project.%i.log.zip" minIndex = 1 maxIndex = 10 } triggeringPolicy(SizeBasedTriggeringPolicy) { maxFileSize = "2MB" } } // specify level logger("com.test.hello", DEBUG) //By default, the level of the root level is set to DEBUG root(DEBUG, ["STDOUT"])
下面开始面相切面编程。
我们想要在消息打印组件的前后做一些工作,但又不想要修改打印组件的内容。那么可以使用@Aspect:
创建:com.test.hello.Monitor.java:
package com.test.hello; 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.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; /** * Created by rmiao on 8/15/2016. */ @Aspect @Component public class Monitor { private final Logger logger = LoggerFactory.getLogger(Monitor.class); @Pointcut("execution(* com.test.hello.MessagePrinter.printMessage())") public void message(){} @Before(value = "message()") public void pre(){ logger.info("before print."); } @After(value = "message()") public void after(){ logger.info("after print."); } }
@Aspect表示这是一个aop切面。等价于xml配置中的<aop>
@Pointcut表示切面的匹配方式
@Before表示切面调用前执行
@After表示切面调用后执行。
要使上述的配置生效,还需开启切面,在配置类中声明@EnableAspectJAutoProxy:
package com.test.hello; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.*; /** * Created by rmiao on 8/15/2016. */ @Configuration @ComponentScan @EnableAspectJAutoProxy public class Application { @Bean MessageService mockMessageService(){ return () -> "Hello World!"; } public static void main(String[] args){ ApplicationContext context = new AnnotationConfigApplicationContext(Application.class); MessagePrinter printer = context.getBean(MessagePrinter.class); printer.printMessage(); } }
运行:
八月 15, 2016 9:13:45 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh 信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@31cefde0: startup date [Mon Aug 15 21:13:45 CST 2016]; root of context hierarchy 21:13:49.278 [main] INFO c.t.h.Monitor - before print. Hello World! 21:13:49.306 [main] INFO c.t.h.Monitor - after print.
唯有不断学习方能改变! -- Ryan Miao