简化java的开发,提高对象的可用性,灵活性。
DI(Dependency Injection),依赖注入,将一个Bean注入到另一个Bean的属性或构造器参数中,将两者进行关联。减少代码的耦合性,举勇者斗恶龙例子。勇者在类中创建了恶龙对象,并通过一个函数进行交互,但是冒险 任务并不是单一的,我们可以救公主,收集材料,制作武器,写死的代码救缺少了灵活性,通过构造器进行注入就能实现任务的传入,提升功能性。
AOP(Aspect Oriented Programming),对于一些系统级别的功能,安全检查(登陆账号的验证),日志记录,事务管理,它们常常在模块各处被调用,
不便于管理,Aop通过声明的方式,将这些功能,借助DI进行使用管理。提高代码的内聚性。举吟游诗人和勇者的例子。没有AOP之前,我们需要在勇者中
声明吟游诗人,然后才能调用歌颂勇者事迹的方法,有了切面之后,在代码中没有显式的调用方法,而是通过xml文档的方式确定执行哪个代码,以及在哪里执行。
配置Spring中的Bean
- 使用xml文档
- 使用JavaConfig,加载第三方库
- 隐式bean发现机制和自动装配,常用的
@ComponentScan的使用
扫描发现当前类所在包下的带有@Component(name)的类,注入成Bean。
属性,basePackages、basePackageClasses的值用来定位扫描的包位置
@Autowired自动装配
当你需要从Spring中获取一个Bean时,用这个注解。不用这个注解会导致获取Bean时出现空指针异常,当出现多个满足条件的Bean时也会出现异常。
@Configuration显式配置
@Configuration
public class RedisConfig {
@Bean
public RedisSerializer<String> redisKeySerializer() {
return new StringRedisSerializer();
}
}
XML配置
构造器注入:有强依赖关系的Bean;
属性注入:不具有强依赖关系的。
区别
1、属性注入需要该Bean包含这些属性的setter方法。
2、与传统的JavaBean的写法更相似,程序开发人员更容易理解、接收。通过setter方法设定依赖关系显得更加直观。
3、对于复杂的依赖关系,如果采用构造器注入,会导致构造器过于臃肿,难以阅读。Spring在创建Bean实例时,同时需要实例化器依赖的全部实例,因而导致性能下降。而使用属性注入,则能避免这些问题。
c命名空间,p命名空间是一种简写方式。
自动装配和显式装配的混合使用
@Import(Config.class)拼接不同的JavaConfig
XML文件中使用import标签装配
按照生产环境配置Bean
使用@Profile(name)注解,可放在类名之上,也可放在方法上,当对应的profile激活时,装配对应的Bean,
主要用于线上环境prod(product)、开发环境dev(development)、测试环境test、提测环境qa、单元测试unitest等。
在application.properties中使用spring.profiles.active=dev
进行配置。
@Configuration
public class AppConfig {
@Conditional(OsxCondition.class)
@Bean
public Student student(){
return new Student();
}
@Conditional(LinuxCondition.class)
@Bean
public Teacher teacher(){
return new Teacher();
}
@Conditional(WindowsCondition.class)
@Bean
public Parent parent(){
return new Parent();
}
@Profile("dev")
@Bean
public Monitors monitors() {
return new Monitors();
}
}
有条件的创建Bean
使用@Conditional(Condition.class),Condition的实现类用于判断当前环境是否可以创建Bean。
处理有歧义的装配
使用@Autowired装配接口时,如果该接口有多个实现类,就会出现歧义问题。
使用@Primary设置优先装配的Bean;
使用@Qualifier(name),表示限定符,可理解为类的某些特性,不可以多个进行叠加。
搭配@Autowired装配对应类名的实现类,最好在@Component也添加@Qualifier(name),保持name的一致。
可使用自定义的限定符注解@Creamy,可重复叠加。
Bean的作用域
单例是默认的装配模式,当类的状态易变时,单例模式不再适合,@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)。
场景举例
会话作用域的购物车的好处,单例会让所有用户的商品加在一个购物车中,原型无法保留用户在不同位置的请求。
懒加载
@Lazy,懒加载针对单例模式而言,容器创建之后,不会立刻注册Bean,只有当Bean第一次被获取时才会进行注册。
@Configuration
public class LazyConfig {
@Bean
@Lazy
public Student getStudent() {
System.out.println("register Bean ... ");
return new Student();
}
}
@SpringBootApplication
public class LazyConfigAppliction {
public static void main(String[] args) {
SpringApplication.run(LazyConfigAppliction.class, args);
ApplicationContext ioc = new AnnotationConfigApplicationContext(LazyConfig.class);
System.out.println("creating ioc ...");
Student student = ioc.getBean(Student.class);
}
}
运行时注入外部文件值
@SpringBootTest
@PropertySource("application-dev.properties")
public class EnvironmentTest {
@Autowired
Environment env;
@Test
public void EnvironmentConfig() {
System.out.println("a:" + env.getProperty("env.a"));
}
}
AOP术语
切面完成的工作被称为通知,确定了切面完成的工作,何时(方法的前后)去完成。
通知类型
连接点(Joint Point)和切入点(Poincut)
潜在的可插入切面的时机点。在哪里插入切面。
引入(Introduction)
引入用于修改当前类的属性和方法。
Spring中仅支持方法级别的连接点。使用AspectJ的切点表达式来定义切点
@ResponseBody:用在controller中,将方法的返回值作为web响应的主体,不解释为视图,通过http消息转换器请求http中的标头内容类型将返回值转换 为http响应主体。
@Target(ElementType.METHOD)
//确定注解的类型,加在方法之上还是啥(这么细,傻逼听不懂,只会ctrl-c)
@Retention(RetentionPolicy.RUNTIME)
//保留的注解,标识注解停留的时间。
@Documented
public @interface LogOperation {
String value() default "";
}
@Aspect
@Component
public class LogOperationAspect {
@Autowired
private SysLogOperationService sysLogOperationService;
//切点,获取注解的位置
//@execution()获取aspectJ切点表达式的位置
@Pointcut("@annotation(io.renren.common.annotation.LogOperation)")
public void logPointCut() {
}
//@Before是在方法执行前,不返回结果
//这里是环绕注解,要返回结果
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
long beginTime = System.currentTimeMillis();
try {
//执行方法
Object result = point.proceed();
//执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
//持久化,保存日志
saveLog(point, time, OperationStatusEnum.SUCCESS.value());
return result;
}catch(Exception e) {
//执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
//保存日志
saveLog(point, time, OperationStatusEnum.FAIL.value());
throw e;
}
}
private void saveLog(ProceedingJoinPoint joinPoint, long time, Integer status) throws Exception {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = joinPoint.getTarget().getClass().getDeclaredMethod(signature.getName(), signature.getParameterTypes());
LogOperation annotation = method.getAnnotation(LogOperation.class);
SysLogOperationEntity log = new SysLogOperationEntity();
if(annotation != null){
//注解上的描述
log.setOperation(annotation.value());
}
//登录用户信息
UserDetail user = SecurityUser.getUser();
if(user != null){
log.setCreatorName(user.getUsername());
}
log.setStatus(status);
log.setRequestTime((int)time);
//请求相关信息
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
log.setIp(IpUtils.getIpAddr(request));
log.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT));
log.setRequestUri(request.getRequestURI());
log.setRequestMethod(request.getMethod());
//请求参数
Object[] args = joinPoint.getArgs();
try{
String params = JsonUtils.toJsonString(args[0]);
log.setRequestParams(params);
}catch (Exception e){
}
//保存到DB
sysLogOperationService.save(log);
}
}
SpringBoot项目打包: IDEA中mvn package,存放在target目录下。