Skip to content

Commit 49ff457

Browse files
committed
Polish Stage 7 Lesson 4
1 parent 8fd8bfb commit 49ff457

File tree

10 files changed

+520
-0
lines changed

10 files changed

+520
-0
lines changed

「一入 Java 深似海 」/代码/segmentfault/deep-in-java/stage-7/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
<module>stage-7-lesson-1</module>
1717
<module>stage-7-lesson-2</module>
1818
<module>stage-7-lesson-3</module>
19+
<module>stage-7-lesson-4</module>
1920
</modules>
2021

2122
</project>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<artifactId>stage-7</artifactId>
7+
<groupId>com.segmentfault</groupId>
8+
<version>1.0-SNAPSHOT</version>
9+
</parent>
10+
<modelVersion>4.0.0</modelVersion>
11+
12+
<artifactId>stage-7-lesson-4</artifactId>
13+
<name>「一入 Java 深似海 」系列 :: 第七期 :: 第三节</name>
14+
15+
<properties>
16+
<java.version>1.8</java.version>
17+
</properties>
18+
19+
<dependencies>
20+
21+
<dependency>
22+
<groupId>org.springframework</groupId>
23+
<artifactId>spring-context</artifactId>
24+
<version>5.1.10.RELEASE</version>
25+
</dependency>
26+
27+
<dependency>
28+
<groupId>org.springframework</groupId>
29+
<artifactId>spring-context-indexer</artifactId>
30+
<version>5.1.10.RELEASE</version>
31+
<optional>true</optional>
32+
</dependency>
33+
34+
</dependencies>
35+
36+
<build>
37+
<plugins>
38+
<plugin>
39+
<groupId>org.apache.maven.plugins</groupId>
40+
<artifactId>maven-compiler-plugin</artifactId>
41+
<configuration>
42+
<encoding>UTF-8</encoding>
43+
<source>${java.version}</source>
44+
<target>${java.version}</target>
45+
</configuration>
46+
</plugin>
47+
</plugins>
48+
</build>
49+
50+
51+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package com.segmentfault.deep.in.java.bytecode.bean;
2+
3+
import org.springframework.cglib.proxy.Enhancer;
4+
import org.springframework.cglib.proxy.MethodInterceptor;
5+
import org.springframework.cglib.proxy.MethodProxy;
6+
7+
import java.beans.*;
8+
import java.lang.reflect.Method;
9+
10+
import static com.segmentfault.deep.in.java.bytecode.bean.Person.isNumeric;
11+
import static java.lang.String.format;
12+
import static java.lang.String.valueOf;
13+
14+
public class CGLibDemo {
15+
16+
public static void main(String[] args) throws Exception {
17+
18+
// CGLIB 代理 Person 类
19+
Person person = (Person) newInstance(Person.class);
20+
21+
// 添加 PropertyChangeListener
22+
person.addPropertyChangeListener(event -> {
23+
// 属性变化通知事件
24+
System.out.printf("监听到属性[%s] 内容变化(事件来源:%s),老值:%s,新值:%s\n",
25+
event.getPropertyName(),
26+
event.getSource(),
27+
event.getOldValue(),
28+
event.getNewValue()
29+
);
30+
});
31+
32+
// 添加 VetoableChangeListener
33+
person.addVetoableChangeListener(event -> {
34+
String newValue = valueOf(event.getNewValue());
35+
if (isNumeric(newValue)) {
36+
throw new PropertyVetoException(
37+
format("当前属性[%s]的新值[%s]不合法,不能为纯数字!", event.getPropertyName(), newValue), event);
38+
}
39+
});
40+
41+
42+
person.setName("小马哥");
43+
person.setName("mercyblitz");
44+
person.setName("123456789");
45+
46+
System.out.println(person);
47+
}
48+
49+
public static Object newInstance(Class<Person> personClass) {
50+
Enhancer enhancer = new Enhancer();
51+
MethodInterceptor methodInterceptor = new PublishingPropertyEventMethodInterceptor();
52+
enhancer.setSuperclass(personClass);
53+
enhancer.setCallback(methodInterceptor);
54+
Object enhancedBean = enhancer.create();
55+
return enhancedBean;
56+
}
57+
58+
}
59+
60+
class PublishingPropertyEventMethodInterceptor implements MethodInterceptor {
61+
62+
private final transient PropertyChangeSupport propertyChangeSupport;
63+
64+
private final transient VetoableChangeSupport vetoableChangeSupport;
65+
66+
PublishingPropertyEventMethodInterceptor() {
67+
this.propertyChangeSupport = new PropertyChangeSupport(this);
68+
this.vetoableChangeSupport = new VetoableChangeSupport(this);
69+
}
70+
71+
@Override
72+
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
73+
74+
// 获取父类 CGLib Class(新生成) extends Person
75+
String methodName = method.getName();
76+
77+
if ("addPropertyChangeListener".equals(methodName)) { // 添加监听器方法
78+
long startTime =System.currentTimeMillis();
79+
addPropertyChangeListener((PropertyChangeListener) args[0]);
80+
long costTime = System.currentTimeMillis() - startTime;
81+
} else if ("addVetoableChangeListener".equals(methodName)) {
82+
addVetoableChangeListener((VetoableChangeListener) args[0]);
83+
} else if ("setName".equals(methodName) && // 方法名判断
84+
void.class.equals(method.getReturnType()) && // 方法返回类型判断
85+
args.length == 1 && // 方法参数数量
86+
args[0] instanceof String // 方法参数类型的判断
87+
) {
88+
// 当 name 属性变化时,发送通知
89+
// 勉强属性(Constrained properties):当属性变化不合适时,阻断属性更新,并且通知异常来说明
90+
String propertyName = "name";
91+
Person person = (Person) object;
92+
String oldValue = person.getName(); // 读取老值
93+
String newValue = (String) args[0]; // 修改后值(newValue)
94+
// 勉强属性(Constrained properties)必须在更新前执行
95+
// 校验规则:当名称为纯数字时,阻断更新
96+
// 当 PropertyVetoException 异常发生时
97+
fireVetoableChange(propertyName, oldValue, newValue);
98+
// 执行 Person.setName(String)
99+
methodProxy.invokeSuper(object, args);
100+
101+
// 发布属性已经变化事件 - PropertyChangeEvent
102+
// 强迫属性(Bound Properties):当属性变化时,强制更新并且发送通知属性变化通知事件
103+
firePropertyChange(propertyName, oldValue, newValue);
104+
105+
return null;
106+
} else { // 其他方法不被拦截,所以直接调用 super 的方法即可
107+
return methodProxy.invokeSuper(object, args);
108+
}
109+
return null;
110+
}
111+
112+
private void firePropertyChange(String propertyName, String oldValue, String newValue) {
113+
// PropertyChangeEvent event = new PropertyChangeEvent(this, propertyName, oldValue, newValue);
114+
// 广播事件
115+
// 得到所有 PropertyChangeEvent 监听器
116+
propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
117+
}
118+
119+
public void fireVetoableChange(String propertyName, Object oldValue, Object newValue) throws PropertyVetoException {
120+
vetoableChangeSupport.fireVetoableChange(propertyName, oldValue, newValue);
121+
}
122+
123+
public void addPropertyChangeListener(PropertyChangeListener listener) {
124+
propertyChangeSupport.addPropertyChangeListener(listener);
125+
}
126+
127+
public void removePropertyChangeListener(PropertyChangeListener listener) {
128+
propertyChangeSupport.removePropertyChangeListener(listener);
129+
}
130+
131+
public PropertyChangeListener[] getPropertyChangeListeners() {
132+
return propertyChangeSupport.getPropertyChangeListeners();
133+
}
134+
135+
public void addVetoableChangeListener(VetoableChangeListener listener) {
136+
vetoableChangeSupport.addVetoableChangeListener(listener);
137+
}
138+
139+
public void removeVetoableChangeListener(VetoableChangeListener listener) {
140+
vetoableChangeSupport.removeVetoableChangeListener(listener);
141+
}
142+
143+
public VetoableChangeListener[] getVetoableChangeListeners() {
144+
return vetoableChangeSupport.getVetoableChangeListeners();
145+
}
146+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package com.segmentfault.deep.in.java.bytecode.bean;
2+
3+
import java.beans.*;
4+
import java.io.Serializable;
5+
import java.lang.reflect.InvocationHandler;
6+
import java.lang.reflect.Method;
7+
import java.lang.reflect.Proxy;
8+
import java.util.EventListener;
9+
10+
import static com.segmentfault.deep.in.java.bytecode.bean.Person.isNumeric;
11+
import static java.lang.String.format;
12+
import static java.lang.String.valueOf;
13+
import static java.lang.reflect.Proxy.newProxyInstance;
14+
15+
public class JavaDynamicProxyDemo {
16+
17+
public static void main(String[] args) throws Exception {
18+
19+
Person person = new Person();
20+
21+
// Java 动态代理
22+
// 动态代理的对象它是 java.lang.reflect.Proxy 的子类(实例),
23+
// 换言之,动态代理的对象所在类是动态合成,它继承了 java.lang.reflect.Proxy 类,同时,实现了多期望的接口列表
24+
// 缺点:只能拦截接口,不能拦截整个类,势必对编程不太友好
25+
Nameable nameable = newInstance(person);
26+
27+
nameable.setName("小马哥");
28+
nameable.setName("mercyblitz");
29+
nameable.setName("123456789");
30+
31+
}
32+
33+
static Nameable newInstance(Person person) {
34+
35+
ClassLoader classLoader = JavaDynamicProxyDemo.class.getClassLoader();
36+
37+
PublishingPropertyEventInvocationHandler handler = new PublishingPropertyEventInvocationHandler(person);
38+
39+
// 添加 PropertyChangeListener
40+
handler.addPropertyChangeListener(event -> {
41+
// 属性变化通知事件
42+
System.out.printf("监听到属性[%s] 内容变化(事件来源:%s),老值:%s,新值:%s\n",
43+
event.getPropertyName(),
44+
event.getSource(),
45+
event.getOldValue(),
46+
event.getNewValue()
47+
);
48+
});
49+
50+
// 添加 VetoableChangeListener
51+
handler.addVetoableChangeListener(event -> {
52+
String newValue = valueOf(event.getNewValue());
53+
if (isNumeric(newValue)) {
54+
throw new PropertyVetoException(
55+
format("当前属性[%s]的新值[%s]不合法,不能为纯数字!", event.getPropertyName(), newValue), event);
56+
}
57+
});
58+
59+
return (Nameable) newProxyInstance(classLoader, new Class[]{Nameable.class, Serializable.class, EventListener.class}, handler);
60+
61+
}
62+
}
63+
64+
class PublishingPropertyEventInvocationHandler implements InvocationHandler {
65+
66+
private final Person person;
67+
68+
private final transient PropertyChangeSupport propertyChangeSupport;
69+
70+
private final transient VetoableChangeSupport vetoableChangeSupport;
71+
72+
PublishingPropertyEventInvocationHandler(Person person) {
73+
this.person = person;
74+
this.propertyChangeSupport = new PropertyChangeSupport(person);
75+
this.vetoableChangeSupport = new VetoableChangeSupport(person);
76+
}
77+
78+
@Override
79+
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
80+
81+
String methodName = method.getName();
82+
83+
if ("setName".equals(methodName) && // 方法名判断
84+
void.class.equals(method.getReturnType()) && // 方法返回类型判断
85+
args.length == 1 && // 方法参数数量
86+
args[0] instanceof String // 方法参数类型的判断
87+
) {
88+
// 当 name 属性变化时,发送通知
89+
// 勉强属性(Constrained properties):当属性变化不合适时,阻断属性更新,并且通知异常来说明
90+
String propertyName = "name";
91+
92+
String oldValue = this.person.getName(); // 读取老值
93+
String newValue = (String) args[0]; // 修改后值(newValue)
94+
// 勉强属性(Constrained properties)必须在更新前执行
95+
// 校验规则:当名称为纯数字时,阻断更新
96+
// 当 PropertyVetoException 异常发生时
97+
fireVetoableChange(propertyName, oldValue, newValue);
98+
// 调整 person.name 内容
99+
this.person.setName(newValue);
100+
// 发布属性已经变化事件 - PropertyChangeEvent
101+
// 强迫属性(Bound Properties):当属性变化时,强制更新并且发送通知属性变化通知事件
102+
firePropertyChange(propertyName, oldValue, newValue);
103+
104+
}
105+
106+
return null;
107+
}
108+
109+
private void firePropertyChange(String propertyName, String oldValue, String newValue) {
110+
// PropertyChangeEvent event = new PropertyChangeEvent(this, propertyName, oldValue, newValue);
111+
// 广播事件
112+
// 得到所有 PropertyChangeEvent 监听器
113+
propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
114+
}
115+
116+
public void fireVetoableChange(String propertyName, Object oldValue, Object newValue) throws PropertyVetoException {
117+
vetoableChangeSupport.fireVetoableChange(propertyName, oldValue, newValue);
118+
}
119+
120+
public void addPropertyChangeListener(PropertyChangeListener listener) {
121+
propertyChangeSupport.addPropertyChangeListener(listener);
122+
}
123+
124+
public void removePropertyChangeListener(PropertyChangeListener listener) {
125+
propertyChangeSupport.removePropertyChangeListener(listener);
126+
}
127+
128+
public PropertyChangeListener[] getPropertyChangeListeners() {
129+
return propertyChangeSupport.getPropertyChangeListeners();
130+
}
131+
132+
public void addVetoableChangeListener(VetoableChangeListener listener) {
133+
vetoableChangeSupport.addVetoableChangeListener(listener);
134+
}
135+
136+
public void removeVetoableChangeListener(VetoableChangeListener listener) {
137+
vetoableChangeSupport.removeVetoableChangeListener(listener);
138+
}
139+
140+
public VetoableChangeListener[] getVetoableChangeListeners() {
141+
return vetoableChangeSupport.getVetoableChangeListeners();
142+
}
143+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.segmentfault.deep.in.java.bytecode.bean;
2+
3+
/**
4+
* 可命名接口
5+
*/
6+
public interface Nameable {
7+
8+
void setName(String name) throws Exception;
9+
}

0 commit comments

Comments
 (0)