Spring
Spring按照模块进行组织
Spring生态
提供大量Spring的项目 更深入地降低我们的开发难度 提高开发效率
- Spring Boot 使用默认开发配置来实现快速开发
构建工具
主流的项目构建工具, Ant、Maven、Gradle
Apache Maven是软件项目管理工具, 基于项目对象模型(POM)管理项目的依赖、编译、文档信息
配置M2_HOME和Path(%M2_HOME%\bin)
可以将本地jar打到本地的Maven仓库
Spring四大原则
- 使用POJO -> 低侵入
- 依赖注入 面向接口 -> 松散耦合
- 切面编程
- 减少样板代码
注解
(元数据 数据的数据)没有实际功能 需要解释为执行某些有意义的动作
- 创建bean @Component @Repository @Controller @Service
- 注入bean @Autowired @Inject @Resource
AOP
- AOP弥补OOP(面向对象编程)的不足
- Spring支持AspectJ的注解式切面编程
- @AspectJ声明一个切面
- @After @Before @Around定义advice 拦截规则(切点)作为参数
- 每一个被拦截处称为连接点(JoinPoint)
注解式拦截能够很好地控制拦截的粒度 更丰富的信息
aop支持及其AspectJ依赖
<!-- spring aop支持-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
<!-- aspectj支持-->
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.5</version>
</dependency>
拦截规则的注解 注解本身没有功能 是元数据(解释数据的数据)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Action {
String name();
}
切面编写 1、注解式拦截 2、方法规则式拦截
package spring.boot.ch1.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;
/**
* @author BobWaters
* @create 2018-06-05 下午11:44
*/
@Aspect
@Component
public class LogAspect {
@Pointcut("@annotation(spring.boot.ch1.aop.Action)")
public void annotationPointCut() {
}
@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("execution(* spring.boot.ch1.aop.DemoMethodService.*(..))")
public void before(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
System.out.println("方法规则式拦截 " + method.getName());
}
}
另外, 配置类上需要增加@EnableAspectJAutoProxy注解, 开启Spring对AspectJ的支持。
@Value注解
- 大部分情形下使用#{}, 只有读取配置文件中的属性时使用${}(//1处)
- 配合使用@PropertySource注解时需要声明PropertySourcesPlaceholderConfigurer这个Bean, 且必须是static(//2处)
- 另外配置文件中的属性还有一种读取方式(//3处)
- 最后配置文件没有加@Configuration(//4处)照常生效
package spring.boot.ch2.spel;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
/**
* @author BobWaters
* @create 2018-06-06 上午9:03
*/
//4
@ComponentScan("spring.boot.ch2.spel")
@PropertySource("classpath:spring/boot/ch2/spel/test.properties")
public class SpelConfig {
@Value("Hello world")
String normal;
@Value("#{systemProperties['os.name']}")
String osName;
@Value("#{T(java.lang.Math).random() * 100}")
String randomNum; //double也可以
@Value("#{demoService.another}")
String fromAnother;
@Value("http://www.baidu.com") //注入网址资源
Resource url;
@Value("classpath:spring/boot/ch2/spel/test.txt") //注入文件资源
Resource txt;
@Value("${program.author}")//1
String authorName;
@Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigurer() { //2
return new PropertySourcesPlaceholderConfigurer();
}
@Autowired
Environment environment; //自动创建了enviroment //3
//@Value("${environment.getProperty('age')}")
//String age; //3
public void outputResource() throws Exception {
System.out.println(normal);
System.out.println(osName);
System.out.println(randomNum);
System.out.println(fromAnother);
System.out.println(IOUtils.toString(url.getInputStream()));
System.out.println(IOUtils.toString(txt.getInputStream()));
System.out.println(authorName);
System.out.println(environment.getProperty("age"));//3
}
}
Bean的自定义操作
引入JSR250-api的jar包
<!-- https://mvnrepository.com/artifact/javax.annotation/javax.annotation-api -->
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.2</version>
</dependency>
如果采用JavaConfig的方式 在@Bean注解中使用initMethod和destroyMethod
如果使用完全注解的方式 那么在对应的方法是加上@PostConstructor @PreDestroy
Profile配置
在对应的@Bean注解上加上@Profile("product")或@Profile("dev")注解
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.getEnvironment().setActiveProfiles("product"); //激活对应环境
context.register(ProfileConfig.class);//加载配置类
context.refresh();
可以配置profile的地方
- servelt2.5 web.xml
- servlet3.0 java文件中设置
- jvm 参数设置环境变量
事件监听
- Event: 继承ApplicationEvent 内含message信息
- Publisher: 内部@Autowired注入一个ApplicationContext 使用ApplicationContext的publishEvent()事件
- Listener: 实现ApplicationListener<T> T为事件类型 实现onApplicationEvent(T t)方法
Spring Aware
实现BeanNameAware可以获取bean的名称 实现ResourceLoaderAware可以获得ResourceLoader
实现ApplicationContext可以获取达到以上所有效果 这些都会与Spring耦合
ResourceLoader的getResource方法可以根据路径参数读取获得Resource对象
使用IOUtils.toString()将resource的getInputStream()输出
多线程与异步
使用基于线程池的任务执行器 ThreadPoolTaskExecutor
- 配置类上加@EnableAsync注解
- 配置类实现AsyncConfigurer(实现两个方法)
- Service类中的异步方法上加@Async注解
package spring.boot.ch3.taskexecutor;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
/**
* @author BobWaters
* @create 2018-06-10 上午12:56
*/
@ComponentScan("spring.boot.ch3.taskexecutor")
@Configuration
@EnableAsync
public class TaskExecutorConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(25);
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
}
}
定时任务
- 配置类使用@EnableScheduling开启
- 定时执行的方法上使用@Scheduled注解修饰 参数可以为cron fixRate等
@Conditional
- 创建若干的条件继承Condition接口
- @Conditional注解使用在@Bean标注的方法上,传入参数是实现Condition接口的类,控制bean的创建
- 另外,容器context可以获得environment继而可以获得os.name等
组合注解
package spring.boot.ch3.annotation;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import java.lang.annotation.*;
/**
* @author BobWaters
* @create 2018-06-10 上午10:53
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ComponentScan
@Configuration
public @interface WiselyConfiguration {
String[] value() default {}; //覆盖value参数
}
@Enable*
每个注解定义上上都有@Import注解,作用是
- 直接导入配置类 例如@EnableScheduling
- 依据条件选择配置类 例如@EnableAsync
- 动态注册Bean 例如@EnableAspectJAutoProxy
集成测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {TConfig.class})
@ActiveProfiles("prod")
- SpringJUnit4ClassRunner提供Junit环境下Spring TestContext Framework的功能
- ContextConfiguration加载ApplicationContext classes指定配置类
- ActiveProfiles声明活动的profile
注意:
- junit至少使用4.12
- spring-test版本不合适会报错
在配置文件里面没有配置
spring.profiles.active=test
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of method sqlSessionFactory in com.sankuai.appflow.eventanalyze.config.OceanDbConfiguration required a bean of type 'javax.sql.DataSource' that could not be found.
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Qualifier(value=oceanDataSource)
The following candidates were found but could not be injected:
- Bean method 'dataSource' in 'JndiDataSourceAutoConfiguration' not loaded because @ConditionalOnProperty (spring.datasource.jndi-name) did not find property 'jndi-name'
- Bean method 'dataSource' in 'XADataSourceAutoConfiguration' not loaded because @ConditionalOnClass did not find required class 'javax.transaction.TransactionManager'
Action:
Consider revisiting the entries above or defining a bean of type 'javax.sql.DataSource' in your configuration.