1、动态代理的几种方式
Java主要有两种代理,JDK和Cglib动态代理。先看JDK代理实例如下:
JDK创建代理有一个限制,即它只能为接口创建代理实例。举个例子如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public
interface
Advice {
void
beforeMethod();
void
afterMethod();
}
public
class
TimeAdvice
implements
Advice {
long
startTime;
long
endTime;
public
void
beforeMethod() {
startTime = System.nanoTime();
// 获取开始时间
System.out.println(
"开始计算程序运行时间"
+ startTime);
}
public
void
afterMethod() {
endTime = System.nanoTime();
// 获取结束时间
System.out.println(
"计算程序运行时间: "
+ (endTime - startTime) +
"ns"
);
}
}
|
1
2
3
4
5
6
7
8
|
public
interface
SalaryInterface {
public
void
doSalary();
}
public
class
Salary
implements
SalaryInterface{
public
void
doSalary() {
System.out.println(
"进行薪资计算的逻辑处理"
);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
import
java.lang.reflect.InvocationHandler;
import
java.lang.reflect.Method;
import
java.lang.reflect.Proxy;
/*
* 每一个代理实例都必须指定一个调用处理器,代理对象调用方法时,
* 该方法会指派到调用处理器的invoke()中去。代理的方法封装成
* invoke中的method对象,其中的参数封装成Object[].
*/
public
class
MyProxy
implements
InvocationHandler{
private
Object obj;
// 希望被代理的对象
private
Advice advice;
// 绑定代理对象
public
Object bind(Object obj, Advice advice) {
this
.obj = obj;
this
.advice = advice;
return
Proxy.newProxyInstance(
obj.getClass().getClassLoader(),
// 类加载器
obj.getClass().getInterfaces(),
// 创建目标类所需要使用的一组接口
this
// 一个实现InvocationHandler的实例,用来整合横切与业务逻辑
);
}
/*
* 实现代理
* method为方法名,args为代理实例某一方法的入参数组,而obj为所属的实例对象
*/
public
Object invoke(Object proxy, Method method, Object[] args)
throws
Throwable {
Object result =
null
;
try
{
advice.beforeMethod();
result = method.invoke(obj, args);
advice.afterMethod();
}
catch
(Exception e){
e.printStackTrace();
}
return
result;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public
class
Bootstrap {
public
static
void
main(String[] args) {
Advice advice =
new
TimeAdvice();
SalaryInterface p =
new
Salary();
// Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象
MyProxy proxy =
new
MyProxy();
SalaryInterface y = (SalaryInterface)proxy.bind(p, advice);
y.doSalary();
// 相当于调用proxy.invoke(proxy, "doSalary, null);
}
}
|
1
2
3
|
开始计算程序运行时间
43041762316001
进行薪资计算的逻辑处理
计算程序运行时间: 882610ns
|
CGLib采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,并顺势织入横切逻辑。
CGGlib创建的代理对象要比JDK的性能高很多,但是创建时所花费的时间却比JDK动态代理要多。所以对于singleton的代理对象或者具有实例池的代码,由于无须频繁创建代码对象,用CGLib比较合适。也就是生命周期长的实例用CGLib比较合适。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
/**
* 使用cglib动态代理
*/
public
class
Cglib
implements
MethodInterceptor {
private
Object target;
long
startTime;
long
endTime;
/**
* 创建代理对象
*/
public
Object getInstance(Object target) {
this
.target = target;
Enhancer enhancer =
new
Enhancer();
enhancer.setSuperclass(
this
.target.getClass());
// 设置需要创建子类的类
enhancer.setCallback(
this
);
// 回调方法
return
enhancer.create();
// 通过字节码技术动态创建子类实例
}
@Override
// 回调方法 ,拦截所有的父类方法调用
public
Object intercept(Object obj, Method method, Object[] args,MethodProxy proxy)
throws
Throwable {
startTime = System.nanoTime();
// 获取开始时间
System.out.println(
"开始计算程序运行时间"
+ startTime);
Object result = proxy.invokeSuper(obj, args);
// 通过代码类调用父类中的方法
endTime = System.nanoTime();
// 获取结束时间
System.out.println(
"计算程序运行时间: "
+ (endTime - startTime) +
"ns"
);
return
result;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
/*
* 采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截
* 所有父类方法的调用,并顺势织入横切逻辑
*
* singletom的代理对象或者具有实例池的代理,因为无须频繁创建代理对象,比较适合用CGLib动态代理技术
*
* 由于CGLib采用动态创建子类的方式生成代理对象,所以不能对目标类中的final,private等方法进行代理
*/
public
class
TestCglib {
public
static
void
main(String[] args) {
Cglib cglib=
new
Cglib();
Salary salary=(Salary)cglib.getInstance(
new
Salary());
salary.doSalary();
}
}
|
2、Spring AOP(Aspect Oriented Programming面向切面编程)与IoC(Inverse of Control控制反转)的实现
Spring AOP主要就是通过动态代理来实现,而Ioc是通过反射来实现,将创建对象和对象之间的依赖管理交给IoC容器来做,完成对象之间的解耦。
举个反射的例子,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
public
class
Car {
private
String brand;
private
String color;
private
int
maxSpeed;
public
Car() {
System.out.println(
"init car!!"
);
}
public
Car(String brand, String color,
int
maxSpeed) {
this
.brand = brand;
this
.color = color;
this
.maxSpeed = maxSpeed;
}
public
void
introduce() {
System.out.println(
"brand:"
+ brand +
";color:"
+ color +
";maxSpeed:"
+ maxSpeed);
}
public
String getBrand() {
return
brand;
}
public
void
setBrand(String brand) {
this
.brand = brand;
}
public
String getColor() {
return
color;
}
public
void
setColor(String color) {
this
.color = color;
}
public
int
getMaxSpeed() {
return
maxSpeed;
}
public
void
setMaxSpeed(
int
maxSpeed) {
this
.maxSpeed = maxSpeed;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
public
class
ReflectTest {
public
static
Car initByDefaultConst()
throws
Throwable {
// loadClass()方法必须使用全额限定名
ClassLoader loader = Thread.currentThread().getContextClassLoader();
// 每一个类在JVM中都拥有一个对应的java.lang.Class对象,用来描述类结构信息
// Class clazz = loader.loadClass("reflect.Car");
Class clazz = Class.forName(
"reflect.Car"
);
Constructor cons = clazz.getDeclaredConstructor((Class[])
null
);
Car car = (Car) cons.newInstance();
Method setBrand = clazz.getMethod(
"setBrand"
, String.
class
);
setBrand.invoke(car,
"红旗CA72"
);
Method setColor = clazz.getMethod(
"setColor"
, String.
class
);
setColor.invoke(car,
"黑色"
);
Method setMaxSpeed = clazz.getMethod(
"setMaxSpeed"
,
int
.
class
);
setMaxSpeed.invoke(car,
200
);
return
car;
}
public
static
Car initByParamConst()
throws
Throwable {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class clazz = loader.loadClass(
"reflect.Car"
);
Constructor cons = clazz.getDeclaredConstructor(
new
Class[] { String.
class
, String.
class
,
int
.
class
});
Car car = (Car) cons.newInstance(
new
Object[] {
"吉利TOPMIX"
,
"绿色"
,
120
});
return
car;
}
public
static
void
main(String[] args)
throws
Throwable {
Car car1 = initByDefaultConst();
Car car2 = initByParamConst();
car1.introduce();
car2.introduce();
}
}
|
1
2
3
|
init car!!
brand:红旗CA72;color:黑色;maxSpeed:
200
brand:吉利TOPMIX;color:绿色;maxSpeed:
120
|
每一个类在JVM中都拥有一个对应的java.lang.Class对象,它提供了类结构信息的描述。数组、枚举、注解甚至void都有对应的Class对象。可以从Class对象中获取构造函数、成员变量、方法等类元素的反射对象,并以编程的方式通过这些反射对目标类对象进行操作。
3、Spring事务的传播行为
Spring中的事务完全基于数据库的事务,如果数据库引擎使用MyISAM引擎,那Spring的事务其实是不起作用的。另外,Spring为开发者提供的与事务相关的特性就是事务的传播行为,如下:
事务传播行为类型 | 说明 |
propagation_required | 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择(Spring默认的事务传播行为) |
propagation_supports | 支持当前事务,如果当前没有事务,就以非事务方式执行 |
propagation_mandatory(托管) | 使用当前的事务,如果当前没有事务,就抛出异常 |
propagation_requireds_new | 新建事务,如果当前存在事务,把当前事务挂起 |
propagation_not_supported | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 |
propagation_never | 以非事务方式执行,如果当前存在事务,则抛出异常 |
propagation_nested | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作,也就是新建一个事务。父事务回滚,嵌套的事务也要回滚。
|
Spring通过事务传播行为控制当前的事务如何传播到被嵌套调用的目标服务接口方法中。
Spring可以配置事务的属性,但是隔离级别、读写事务属性、超时时间与回滚设置等都交给了JDBC,真正自己实现的只有事务的传播行为。那么什么时候发生事务的传播行为呢?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public
class
ForumService {
private
UserService userService;
@Transactional
(propagation = Propagation.REQUIRED)
public
void
addTopic() {
// add Topic
this
.updateTopic();
userService.addCredits();
}
@Transactional
(propagation = Propagation.REQUIRED)
public
void
updateTopic() {
// add Topic
}
public
void
setUserService(UserService userService) {
this
.userService = userService;
}
}
|
看一下userService中的addCredits()方法,如下:
1
2
3
4
5
6
7
|
public
class
UserService {
@Transactional
(propagation = Propagation.REQUIRES_NEW)
public
void
addCredits() {
}
}
|
然后测试下:
1
|
forumService.addTopic();
|
开启了Spring4日志的DEBUG模式后,输出如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
- Returning cached instance of singleton bean
'txManager'
- Creating
new
transaction with name [com.baobaotao.service.ForumService.addTopic]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT;
''
- Acquired Connection [org.apache.commons.dbcp.PoolableConnection
@1948ea69
]
for
JDBC transaction
- Switching JDBC Connection [org.apache.commons.dbcp.PoolableConnection
@1948ea69
] to manual commit
- Suspending current transaction, creating
new
transaction with name [com.baobaotao.service.UserService.addCredits]
- Acquired Connection [org.apache.commons.dbcp.PoolableConnection
@773e2eb5
]
for
JDBC transaction
- Switching JDBC Connection [org.apache.commons.dbcp.PoolableConnection
@773e2eb5
] to manual commit
- Initiating transaction commit
- Committing JDBC transaction on Connection [org.apache.commons.dbcp.PoolableConnection
@773e2eb5
]
- Releasing JDBC Connection [org.apache.commons.dbcp.PoolableConnection
@773e2eb5
] after transaction
- Returning JDBC Connection to DataSource
- Resuming suspended transaction after completion of inner transaction
- Initiating transaction commit
- Committing JDBC transaction on Connection [org.apache.commons.dbcp.PoolableConnection
@1948ea69
]
- Releasing JDBC Connection [org.apache.commons.dbcp.PoolableConnection
@1948ea69
] after transaction
- Returning JDBC Connection to DataSource
|
清楚的看到调用addCredis()方法时创建了一个新的事务,而在这个方法中调用addCredits()方法时,由于这个方法的事务传播行为为progation_required_new,所以挂起了当前的线程,又创建了一个新的线程。但是对于this.updateTopic()方法调用时,由于这个
方法的事务仍然为propagation_required,所以在当前线程事务中执行即可。
在使用事务中我们需要做到尽量避免死锁、尽量减少阻塞,根据不同的数据库设计和性能要求进行所需要的隔离级别,才是最恰当的。具体以下方面需要特别注意:
A、 事务操作过程要尽量小,能拆分的事务要拆分开来
B、 事务操作过程不应该有交互(系统交互,接口调用),因为交互等待的时候,事务并未结束,可能锁定了很多资源
C、 事务操作过程要按同一顺序访问对象。(避免死锁的情况产生)
D、 提高事务中每个语句的效率,利用索引和其他方法提高每个语句的效率可以有效地减少整个事务的执行时间。
E、 查询时可以用较低的隔离级别,特别是报表查询的时候,可以选择最低的隔离级别(未提交读)。
4、spring是如何做到为普通类创建单例的呢?
参考:http://blog.youkuaiyun.com/arvinrong/article/details/7756167
5、Spring MVC
SpringMVC设计思路:将整个处理流程规范化,并把每一个处理步骤分派到不同的组件中进行处理。
这个方案实际上涉及到两个方面:
l 处理流程规范化 —— 将处理流程划分为若干个步骤(任务),并使用一条明确的逻辑主线将所有的步骤串联起来
l 处理流程组件化 —— 将处理流程中的每一个步骤(任务)都定义为接口,并为每个接口赋予不同的实现模式
处理流程规范化是目的,对于处理过程的步骤划分和流程定义则是手段。因而处理流程规范化的首要内容就是考虑一个通用的Servlet响应程序大致应该包含的逻辑步骤:
l 步骤1—— 对Http请求进行初步处理,查找与之对应的Controller处理类(方法) ——HandlerMapping
l 步骤2—— 调用相应的Controller处理类(方法)完成业务逻辑 ——HandlerAdapter
l 步骤3—— 对Controller处理类(方法)调用时可能发生的异常进行处理 ——HandlerExceptionResolver
l 步骤4—— 根据Controller处理类(方法)的调用结果,进行Http响应处理 ——ViewResolver
正是这基于组件、接口的设计,支持了SpringMVC的另一个特性:行为的可扩展性。