概念
IOC,Inversion of Control的缩写,即“控制反转”的意思,它还有个别名叫依赖注入(DI)。
如何理解“控制反转”,举个例子:
由此可见,引入IOC容器后,对象A使用对象B,控制权限由主动变成了被动,这就是“控制反转”。
如何理解(“通过引入IOC容器,利用依赖关系注入的方式,实现对象之间的解耦”):在传统的实现中,组件(对象)之间的依赖,通过new关键字实现组件之间关系的组合,这种由程序内部代码实现的方式,造成了组件之间的耦合,而IOC正好可以解决组件之间的耦合,实现解耦。通俗的讲,IOC就是一个对象制造工厂,你需要什么,它就给你什么,你直接使用,不用关心如何生成,如何销毁,IOC都帮你完成了,也就是说由容器在运行期将组件间的某种依赖关系动态注入组件中。
应用
//在以前传统的写法中,调用一个接口的某个方法
xxxService xxx=new xxxServiceImpl();
xxx.method();
//假如这个接口实现类别人还没实现,这样写程序在编译时就会出错
//如何解决这种编译依赖呢?
//答案是:反射,利用反射机制,实现调用他人为实现的接口,从而达到编译时通过。
public class MyBeanFactory{
//读取bean.properties,只能读取properties文件,只读
private static ResourceBundle bundle = ResourceBundle.getBundle("bean");
private static Map<String, Object> beans = new HashMap<>();
static {
try {
Enumeration<String> keys = bundle.getKeys();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
String beanPath = bundle.getString(key);
Object value = Class.forName(beanPath).newInstance();
beans.put(key, value);
}
} catch (Exception e) {
throw new ExceptionInInitializerError("容器初始化失败");
}
}
public static Object getBean(String beanName) {
return beans.get(beanName);
}
public static void main(String[] args) {
xxxService xxx = (xxxService) MyBeanFactory.getBean("xxxService");
xxx.method();
//传统方式
//xxxService xxx=new xxxServiceImpl();
//xxx.method();
}
}
bean.properties文件:
xxxService=com.a.b.xxxServiceImpl
//利用反射机制,使强依赖变成弱依赖(削减耦合),这也是springIOC的简单实现。
spring常用注解:
@Controller 用于标注控制器层组件
@Service 用于标注业务层组件
@Repository 用于标注数据层(DAO层)组件
@Component 用于标注这是一个受spring容器管理的组件,组件引用的名称是类名,第一个字母小写,或者使用@Component(“beanID”)指定组件名
@Bean 方法级别的注解,一般用于@Configuration和@Component注解的类中,@Bean注解的方法产生一个Bean对象,由spring容器管理,并放到IOC中,引用名是方法名或者使用@Bean(name=“beanID”)指定组件名。一般是在需要使用第三方的对象时,比如使用redisTemplate
@Scope 用于改变Bean的作用范围,value值singleton、prototype、request、session,使用方式@Scope(“prototype”)
singleton:单例,默认,在初始化时生成单例bean对象
prototype:原型(多例),在初始化时不生成bean对象,使用时返回不同bean对象。也就是保证每一个请求都会有不同的action来处理,避免action线程问题
request:web环境下每一个request请求都会返回一个不同的bean,只在本次请求中有效
session:web环境下每一个request请求都会返回一个不同的bean,在session中有效
由于Spring默认是单例的,只会创建一个action对象,每次访问都是同一个对象,容易产生并发问题,数据不安全。
@Autowired 该配置属于spring的,默认按类型进行自动装配。在容器查找匹配的Bean,当有且仅有一个匹配的Bean时,Spring将其注入@Autowired标注的变量中
@REsource 该配置属于J2EE的,默认按名称进行自动装配,当找不到与名称匹配的Bean时会按类型装配
依赖注入:
spring的依赖注入方式主要有4种,注解注入、set注入、构造器注入、静态工厂注入。推荐使用注解注入,配置较少。
基于注解注入:
//服务层代码
@Service
public class XxxServiceImpl implements XxxService{
}
//控制层代码
@Controller
public class XxxController{
//@Autowired与@Resource都可以用来装配Bean,都可以写在字段、setter方法上。他们的区别是:
//@Autowired默认按类型进行自动装配(该注解属于Spring),默认情况下要求依赖对象必须存在,如果要允许为null,需设置required属性为false,例:@Autowired(required=false)。如果要使用名称进行装配,可以与@Qualifier注解一起使用
@Autowired
@Qualifier("xxxService")
//@Resource(name="xxxService")
private XxxService xxxService;
}
概念
AOP:Aspect Oriented Programming 面向切面编程。
主要作用是将各个业务模块相同部分却又不影响业务逻辑的功能代码,抽离出来。常用于:日志记录,性能统计,安全控制,事务处理,异常处理等。
AOP关键名词:
切面(Aspect):共有功能的实现。如日志切面、权限切面、验签切面等。在实际开发中通常是一个存放共有功能实现的标准Java类。当Java类使用了@Aspect注解修饰时,就能被AOP容器识别为切面。
通知(Advice):切面的具体实现。就是要给目标对象织入的事情。以目标方法为参照点,根据放置的地方不同,可分为前置通知(Before)、后置通知(AfterReturning)、异常通知(AfterThrowing)、最终通知(After)与环绕通知(Around)5种。在实际开发中通常是切面类中的一个方法,具体属于哪类通知,通过方法上的注解区分。
连接点(JoinPoint):程序在运行过程中能够插入切面的地点。例如,方法调用、异常抛出等。Spring只支持方法级的连接点。一个类的所有方法前、后、抛出异常时等都是连接点。
切入点(Pointcut):用于定义通知应该切入到哪些连接点上。不同的通知通常需要切入到不同的连接点上,这种精准的匹配是由切入点的正则表达式来定义的。
应用
继续在我之前的代码中(SpringCloud学习),利用AOP特性,实现日志记录功能:
1.实现自定义日志操作注解
2.定义操作日志切面
@Component
@Aspect
public class LogAspect {
/**
* 定义切入点
*/
@Pointcut("@annotation(com.eb.gc.eborderserver.annotation.Log)")
public void BrokerAspect() {
}
/**
* 环绕通知
*/
@Around("BrokerAspect()")
public Object doAround(ProceedingJoinPoint point) throws Throwable {
Object res;
Long time = System.currentTimeMillis();
try {
res = point.proceed();
time = System.currentTimeMillis() - time;
addOperationLog(point, null, time);
} catch (Exception e) {
addOperationLog(point, e, time);
throw e;
}
return res;
}
/**
* 添加日志
*
* @param joinPoint
* @param e
* @param time
*/
protected void addOperationLog(final JoinPoint joinPoint, final Exception e, final Long time) {
Log annotationLog = getAnnotationLog(joinPoint);
if (annotationLog == null)
return;
if (e != null)
System.out.println(String.format("-----保存日志:模块[%s],操作[%s],异常[%s]", annotationLog.title(),
annotationLog
.businessType().name(), e.getMessage()));
else
System.out.println(String.format("-----保存日志:模块[%s],操作[%s],耗时[%d]", annotationLog.title(), annotationLog
.businessType().name(), time));
}
/**
* 获得注解
*
* @param joinPoint
* @return
*/
private Log getAnnotationLog(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method == null)
return null;
return method.getAnnotation(Log.class);
}
}
参考《深入理解Spring两大特性:IoC和AOP》《Spring IOC》
代码已上传GitHub:https://github.com/Jesssssfish/ebusinessDemo
谢谢。