2. [文件] spring源码分析之AOP.txt ~ 15KB 下载(166)
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
|
spring源码解析 spring---------- ---bean
---aop
1.什么是AOP OOP 面向对象编程 AOP 面向切面,横切逻辑,用于在代码中插入公共片段 SOA service-oriented architecture 面向服务架构 AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码
* 传统方式,复用代码,继承, 扩展代码, 方法覆盖 * 将复用代码抽取出来, 通过代理机制,将抽取代码 应用到目标对象 * AOP 针对目标类 的方法进行 动态代理, 对原有方法 进行拦截增强 1.1 AOP中的术语 Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点. Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义. Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能) Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field. Target(目标对象):代理的目标对象 Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程. spring采用动态代理织入,而AspectJ采用编译期织入和类装在期织入
Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类 Aspect(切面): 是切入点和通知(引介)的结合 1.2Spring AOP底层实现 第一种 JDK 自带 动态代理技术
* JDK 动态代理 必须 基于 接口 进行代理
* 使用代理,对目标对象 进行性能监控(计算运行时间)、安全检查(是否具有权限)、 记录日志
* 注意: 必须要有接口 才能进行代理, 代理后对象必须转换为接口类型 !! 第二种 CGLIB(CodeGenerationLibrary)是一个开源项目 * Spring使用CGlib 进行AOP代理, hibernate 也是支持CGlib(默认 使用 javassist )
需要下载cglib 的jar包 (Spring 最新版本3.2 内部已经集成cglib ,无需下载cglib的jar )
* 可以为目标类,动态创建子类,对目标类方法进行代理 (无需接口)
Spring AOP 底层,会判断用户是根据接口代理还是目标类代理,如果针对接口代理 使用JDK代理,如果针对目标类代理 使用Cglib代理,无法复写final类,另外cglib二进制发型包放在classpath 2.使用注解完成spring AOP AOP 规范由 AOP 联盟组织(aopalliance) 来定义 , 最初目的采用代理方式,对目标方法进行增强 定义了五种增强方式 (Advice ) 1) 前置通知 org.springframework.aop.MethodBeforeAdvice 在目标业务方法执行前 通知代码 (增强代码)
2) 后置通知 org.springframework.aop.AfterReturningAdvice 在目标业务方法返回后,执行通知代码
3) 环绕通知 org.aopalliance.intercept.MethodInterceptor 可以在 目标业务方法执行前 和 执行后 执行通知代码
4) 异常抛出通知 org.springframework.aop.ThrowsAdvice 在目标方法执行出现异常后,执行通知代码
5) 引介通知 org.springframework.aop.IntroductionInterceptor * 与前四种不同,对目标类添加新的方法或者属性
* 前四种都是方法级别的增强,引介通知是类级别的增强
2.0准备spring包含的jar包 2.1编写业务类 package cn.jfree.aspectj; public class UserDao { public void save(){
System.out.println("添加用户");
}
public void update(){
System.out.println("更新用户");
}
public void delete(){
System.out.println("删除用户");
}
public void find(){
System.out.println("查询用户");
}
} 2.2 编写切面类 /************** 详细讲解: 1.类加注解@Aspect
2.切入点表达式execution(*返回值 类名*.方法名*(..))
3.before可以有参数JoinPoint
4.@AfterReturning returning必须和参数名称一致
**************/ package cn.jfree.aspectj; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class AspectJTest { /**
* 使用这种方式完成切入点配置
*/
@Pointcut("execution(* cn.jfree.aspectj.UserDao.delete(..))")
public void pointcut() {
}
/**
* 配置切入点
*/
@Before("execution(* cn.jfree.aspectj.UserDao.save(..))")
public void before() {
System.out.println("在保存之前执行");
}
/**
*
* @param joinpoint --切入点表达式的值
*/
@Before("execution(* cn.jfree.aspectj.UserDao.save(..))")
public void before(JoinPoint joinpoint) {
System.out.println("在保存之前执行"+joinpoint);
}
/**
*
* @param joinpoint
* @param returnVal 返回值必须与参数类型一致
*/
@AfterReturning(value="execution(* cn.jfree.aspectj.UserDao.update(..))",returning="returnVal")
public void afterReturning(JoinPoint joinpoint,Object returnVal){
System.out.println("结果的返回值"+returnVal);
}
/**
* 利用环绕通知可以实现日志管理,性能检测等...
* @param point
* @return
*/
@Around("execution(* cn.jfree.aspectj.UserDao.find(..))")
public Object around(ProceedingJoinPoint point) {
long start = System.currentTimeMillis();
System.out.println("方法执行前时间"+start);
Object obj = null;
try {
obj = point.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("方法结束时间"+end);
long exetime=end-start;
System.out.println("方法执行时间"+exetime);
return obj;
}
/**
* 用于抛出异常
* @param e
*/
@AfterThrowing(value="execution(* cn.jfree.aspectj.UserDao.delete(..))",throwing="e")
public void afterThrow(Throwable e){
System.out.println(e.getMessage());
//此处如果出现异常,不会执行
}
/**
* 不管是否出现异常都会执行,相当于finally
*
*/
@After("AspectJTest.pointcut()")
public void after(){
System.out.println("不管是否出现异常都会执行,相当于finally");
}
} 2.3 applicationContext.xml配置文件 <!-- 开启自动代理 --> <aop:aspectj-autoproxy/> <!-- 业务类配置 --> <bean id="userDao" class="cn.jfree.aspectj.UserDao"></bean> <!-- 切面类配置 --> <bean id="myAspectJ" class="cn.jfree.aspectj.AspectJTest"></bean> 2.4 编写测试类 package cn.jfree.aspectj; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class AopTest { public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao=(UserDao) applicationContext.getBean("userDao");
//userDao.save();
// userDao.find();
// userDao.update();
userDao.delete();
}
} 3.Spring 动态AOP自定义标签 3.1 在Spring中自定义注解标签 package org.springframework.aop.config; public class AopNamespaceHandler extends NamespaceHandlerSupport { /**
* Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
* '<code>config</code>', '<code>spring-configured</code>', '<code>aspectj-autoproxy</code>'
* and '<code>scoped-proxy</code>' tags.
*/
public void init() {
// In 2.0 XSD as well as in 2.1 XSD.
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
// Only in 2.0 XSD: moved to context namespace as of 2.1
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
} 3.2 AspectJAutoProxyBeanDefinitionParser方法对aspectj-autoproxy进行解析 package org.springframework.aop.config; class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser { //<aop:aspectj-autoproxy expose-proxy="false" proxy-target-class="false"/>
public BeanDefinition parse(Element element, ParserContext parserContext) {
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
extendBeanDefinition(element, parserContext);
return null;
}
private void extendBeanDefinition(Element element, ParserContext parserContext) {
BeanDefinition beanDef =
parserContext.getRegistry().getBeanDefinition(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
if (element.hasChildNodes()) {
addIncludePatterns(element, parserContext, beanDef);
}
}
private void addIncludePatterns(Element element, ParserContext parserContext, BeanDefinition beanDef) {
ManagedList<TypedStringValue> includePatterns = new ManagedList<TypedStringValue>();
NodeList childNodes = element.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node node = childNodes.item(i);
if (node instanceof Element) {
Element includeElement = (Element) node;
TypedStringValue valueHolder = new TypedStringValue(includeElement.getAttribute("name"));
valueHolder.setSource(parserContext.extractSource(includeElement));
includePatterns.add(valueHolder);
}
}
if (!includePatterns.isEmpty()) {
includePatterns.setSource(parserContext.extractSource(element));
beanDef.getPropertyValues().add("includePatterns", includePatterns);
}
}
} 3.3 注册类 public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
//注册AutoProxyCreator
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
//属性 proxy-target-class expose-proxy
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
//注册组件,用于监听
registerComponentIfNecessary(beanDefinition, parserContext);
}
private static BeanDefinition registerOrEscalateApcAsRequired(Class cls, BeanDefinitionRegistry registry, Object source) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
//如果存在internalAutoProxyCreator注册类,根据类名判断
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
//如果与当前类名不一致,需要根据优化级来判断选择哪个
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {//注册类的类名优先级
apcDefinition.setBeanClassName(cls.getName());
}
}
//如果已经存在注册类,则不创建
return null;
}
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
强制使用cglib需要使用proxy-target-class="true" 3.4 接口BeanPostProcessor 真正创建代理是从getAdvicesAndAdvisorsForBean开始,
获取代理的步骤:1)获取增强方法或者增强器 2)根据获取的增强创建代理
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
//根据给定的beanClass和beanName生成key,格式为beanClass_beanName
Object cacheKey = getCacheKey(bean.getClass(), beanName);
//如果合适做代理,请生成代理对象
if (!this.earlyProxyReferences.containsKey(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { //如果已经处理过了,无需增强
if (this.targetSourcedBeans.containsKey(beanName)) {
return bean;
}
//无需增强
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
//如果是基础设施类,或者配置了无需增强
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
//如果获取增强方法,请创建代理
// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
3.5 获取增强类 1)获取所有增强
2)获取所有增强中适合bean的增强,并应用
protected List<Advisor> findEligibleAdvisors(Class beanClass, String beanName) {
List<Advisor> candidateAdvisors = findCandidateAdvisors();
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
实现思路: 1)获取所有的beanName,在beanFactory中注册的所有类都找出来 2)遍历所有的beanName,找到@Aspect的增强的类 3)对标记为@Aspect进行增强器的提取 4)将提取结果加入到缓存中 public List<Advisor> buildAspectJAdvisors() { List<String> aspectNames = null;
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new LinkedList<Advisor>();
aspectNames = new LinkedList<String>();
String[] beanNames =
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
for (String beanName : beanNames) {
if (!isEligibleBean(beanName)) {
continue;
}
// We must be careful not to instantiate beans eagerly as in this
// case they would be cached by the Spring container but would not
// have been weaved
Class beanType = this.beanFactory.getType(beanName);
if (beanType == null) {
continue;
}
if (this.advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
if (this.beanFactory.isSingleton(beanName)) {
this.advisorsCache.put(beanName, classAdvisors);
}
else {
this.aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);
}
else {
// Per target or per this.
if (this.beanFactory.isSingleton(beanName)) {
throw new IllegalArgumentException("Bean with name '" + beanName +
"' is a singleton, but aspect instantiation model is not singleton");
}
MetadataAwareAspectInstanceFactory factory =
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
this.aspectFactoryCache.put(beanName, factory);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
}
this.aspectBeanNames = aspectNames;
return advisors;
}
}
if (aspectNames.isEmpty()) {
return Collections.EMPTY_LIST;
}
List<Advisor> advisors = new LinkedList<Advisor>();
for (String aspectName : aspectNames) {
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
}
else {
MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
return advisors;
}
|
3. [文件] springORM源码分析.txt ~ 133B 下载(79)
1
2
3
4
5
6
7
8
|
1.JDBC连接数据库 以Mysql为例 1)获取jar包 mysql-conector-java*.jar 2)注册驱动 3)获取链接 4)获取statement 5)执行sql语句 5)关闭流 |
4. [文件] JDBC.txt ~ 6KB 下载(81)
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
|
1.JDBC批处理 l业务场景:当需要向数据库发送一批SQL语句执行时,应避免向数据库一条条的发送执行,而应采用JDBC的批处理机制,以提升执行效率。 l实现批处理有两种方式,第一种方式: ?Statement.addBatch(sql) l执行批处理SQL语句 ?executeBatch()方法:执行批处理命令 ?clearBatch()方法:清除批处理命令 Connection conn = null; Statement st = null; ResultSet rs = null; try { conn = JdbcUtil.getConnection(); String sql1 = "insert into user(name,password,email,birthday) values('kkk','123','abc@sina.com','1978-08-08')";
String sql2 = "update user set password='123456' where id=3"; st = conn.createStatement(); st.addBatch(sql1); //把SQL语句加入到批命令中 st.addBatch(sql2); //把SQL语句加入到批命令中 st.executeBatch(); } finally{ JdbcUtil.free(conn, st, rs);
} l采用Statement.addBatch(sql)方式实现批处理: ?优点:可以向数据库发送多条不同的SQL语句。 ?缺点: ?SQL语句没有预编译。 ?当向数据库发送多条语句相同,但仅参数不同的SQL语句时,需重复写上很多条SQL语句。例如: Insert into user(name,password) values(‘aa’,’111’);
Insert into user(name,password) values(‘bb’,’222’);
Insert into user(name,password) values(‘cc’,’333’);
Insert into user(name,password) values(‘dd’,’444’);
l实现批处理的第二种方式: ?PreparedStatement.addBatch() conn = JdbcUtil.getConnection(); String sql = "insert into user(name,password,email,birthday) values(?,?,?,?)"; st = conn.prepareStatement(sql); for(int i=0;i<50000;i++){ st.setString(1, "aaa" + i); st.setString(2, "123" + i); st.setString(3, "aaa" + i + "@sina.com"); st.setDate(4,new Date(1980, 10, 10)); st.addBatch(); if(i%1000==0){ st.executeBatch(); st.clearBatch(); } } st.executeBatch(); l采用PreparedStatement.addBatch()实现批处理 ?优点:发送的是预编译后的SQL语句,执行效率高。 ?缺点:只能应用在SQL语句相同,但参数不同的批处理中。因此此种形式的批处理经常用于在同一个表中批量插入数据,或批量更新表的数据。 2)关于批处理的思考 在此笔记里,我们将看到我们如何可以使用像Statement和PreparedStatement JDBC API来批量在任何数据库中插入数据。此外,我们将努力探索一些场景,如在内存不足时正常运行,以及如何优化批量操作。 首先,使用Java JDBC基本的API批量插入数据到数据库中。 Simple Batch - 简单批处理 我把它叫做简单批处理。要求很简单,执行批量插入列表,而不是为每个INSERT语句每次提交数据库,我们将使用JDBC批处理操作和优化性能。
想想一下下面的代码: Bad Code String [] queries = {
"insert into employee (name, city, phone) values ('A', 'X', '123')",
"insert into employee (name, city, phone) values ('B', 'Y', '234')",
"insert into employee (name, city, phone) values ('C', 'Z', '345')",
};
Connection connection = new getConnection(); Statement statemenet = connection.createStatement(); for (String query : queries) {
statemenet.execute(query);
} statemenet.close(); connection.close(); 这是糟糕的代码。它单独执行每个查询,每个INSERT语句的都提交一次数据库。考虑一下,如果你要插入1000条记录呢?这是不是一个好主意。 下面是执行批量插入的基本代码。来看看: Good Code Connection connection = new getConnection(); Statement statemenet = connection.createStatement(); for (String query : queries) {
statemenet.addBatch(query);
} statemenet.executeBatch(); statemenet.close(); connection.close(); 请注意我们如何使用addBatch()方法,而不是直接执行查询。然后,加入所有的查询,我们使用statement.executeBatch()方法一次执行他们。没有什么花哨,只是一个简单的批量插入。 请注意,我们已经从一个String数组构建了查询。现在,你可能会想,使其动态化。例如: import java.sql.Connection; import java.sql.Statement; //... Connection connection = new getConnection(); Statement statemenet = connection.createStatement(); for (Employee employee: employees) { String query = "insert into employee (name, city) values('"
+ employee.getName() + "','" + employee.getCity + "')";
statemenet.addBatch(query);
} statemenet.executeBatch(); statemenet.close(); connection.close(); 请注意我们是如何从Employee对象中的数据动态创建查询并在批处理中添加,插入一气呵成。完美!是不是? 等等......你必须思考什么关于SQL注入?这样动态创建的查询SQL注入是很容易的。并且每个插入查询每次都被编译。 为什么不使用PreparedStatement而不是简单的声明。是的,这是个解决方案。下面是SQL注入安全批处理。 SQL Injection Safe Batch - SQL注入安全批处理 思考一下下面代码: import java.sql.Connection; import java.sql.PreparedStatement; //...
String sql = "insert into employee (name, city, phone) values (?, ?, ?)"; Connection connection = new getConnection(); PreparedStatement ps = connection.prepareStatement(sql); for (Employee employee: employees) {
ps.setString(1, employee.getName());
ps.setString(2, employee.getCity());
ps.setString(3, employee.getPhone());
ps.addBatch();
} ps.executeBatch(); ps.close(); connection.close(); 看看上面的代码。漂亮。我们使用的java.sql.PreparedStatement和在批处理中添加INSERT查询。这是你必须实现批量插入逻辑的解决方案,而不是上述Statement那个。 这一解决方案仍然存在一个问题。考虑这样一个场景,在您想要插入到数据库使用批处理上万条记录。嗯,可能产生的OutOfMemoryError: java.lang.OutOfMemoryError: Java heap space com.mysql.jdbc.ServerPreparedStatement$BatchedBindValues.<init>(ServerPreparedStatement.java:72) com.mysql.jdbc.ServerPreparedStatement.addBatch(ServerPreparedStatement.java:330) org.apache.commons.dbcp.DelegatingPreparedStatement.addBatch(DelegatingPreparedStatement.java:171) 这是因为你试图在一个批次添加所有语句,并一次插入。最好的办法是将执行分批次。看看下面的解决方案 Smart Insert: Batch within Batch - 智能插入:将整批分批 这是一个简单的解决方案。考虑批量大小为1000,每1000个查询语句为一批插入提交。 String sql = "insert into employee (name, city, phone) values (?, ?, ?)"; Connection connection = new getConnection(); PreparedStatement ps = connection.prepareStatement(sql); final int batchSize = 1000; int count = 0; for (Employee employee: employees) { ps.setString(1, employee.getName());
ps.setString(2, employee.getCity());
ps.setString(3, employee.getPhone());
ps.addBatch();
if(++count % batchSize == 0) {
ps.executeBatch();
}
} ps.executeBatch(); // insert remaining records ps.close(); connection.close(); 这才是理想的解决方案,它避免了SQL注入和内存不足的问题。看看我们如何递增计数器计数,一旦BATCHSIZE 达到 1000,我们调用executeBatch()提交。 |
5. [文件] spring框架的使用.txt ~ 32KB 下载(72)
6. [文件] SpringMVC源码.txt ~ 7KB 下载(54)
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
|
1.什么是SpringMVC SpringMVC模式提供了构建web的mvc模板,支持策略模式,支持多种视图技术,例如JSP,Velocity,Tiles ,iText和POI.SpringMVC分离了控制器,模板对象,分派器,以及处理程序对象的角色,这种分离可以使它们更容易定制. 2.Spring的概述 SpringMVC是基于Servlet功能实现的,通过实现Servlet接口的DispatcherServlet来封装核心功能实现,通过请求分派给处理程序,同时带有可配置的处理程序映射,视图解析,本地语言,主题解析,文件上传支持.默认的处理程序是非常简单的Controller接口,只有一个方法ModelAndVide handlerRequest(request,response),Spring提供了一个控制器层次结构,可以派生子类.如果应用程序需要处理用户输入表单,可以继承AbstractFormController.如果需要要把多页输入处理到一个表单,可以继承AbstractWizardFormContrloller. SpringMVC从请求中搜集数据,对传入的参数进行验证,Controller对系统流程进行控制管理,将结果返回不同的视图,不同的视图提供不同的方案,拦截器,上传文件等功能 核心组件有: DispatcherServlet Controller handlerMapping ModelAndViewViewResolverInterceptors 3.SpringMVC的解决的问题 1)将web页面的请求传给服务器 2)根据不同的请求结果处理不同的逻辑单元 3)返回处理结果数据并跳转至响应的页面 3.1 SpringMVC的工作流程 1. 用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获;
2. DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;
3. DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(...)方法)
4. 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
5. Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;
6. 根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet ;
7. ViewResolver 结合Model和View,来渲染视图
8. 将渲染结果返回给客户端。
//思考 :SpringMVC为什么使用一个DispacherServlet来处理所有servlet请求 前端控制器模式
4.SpringMVC快速体验 1)在web.xml中配置dispatcherServlet
<!-- 配制spring分发器servlet -->
<servlet> <servlet-name>action</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping> 2)创建Spring配置文件/WEB-INF/action-servlet.xml
Spring mvc需要一个配置文件:
位置:默认情况下在WEB-INF下
命名规则:servlet的名称 + “-”+ servlet.xml
5.SpringMVC的DispatcherServlet解析 6. 初始化 protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
1)初始化MultipartResolver 在Spring中,MultipartResolver主要用于文件上传.默认情况下Spring中是没有Multipart请求处理的,因为一些开发者想要自己处理这些请求,如果想要使用Multipart,需要在配置文件中添加MultipartResolver解析器,如果添加配置则会用于处理Multipart的能力,还能实现用户的文件上传等功能. MultipartResolver就是在initMultipartResolver中加入到DispatcherServlet中的 /**
* Initialize the MultipartResolver used by this class.
* <p>If no bean is defined with the given name in the BeanFactory for this namespace,
* no multipart handling is provided.
//如果不提供处理器,则不会产生对应的multipart处理器
*/
private void initMultipartResolver(ApplicationContext context) {
try {
this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
if (logger.isDebugEnabled()) {
logger.debug("Using MultipartResolver [" + this.multipartResolver + "]");
}
}
catch (NoSuchBeanDefinitionException ex) {
// Default is no multipart resolver.
this.multipartResolver = null;
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate MultipartResolver with name '" + MULTIPART_RESOLVER_BEAN_NAME +
"': no multipart request handling provided");
}
}
}
因为之前已经完成配置文件的解析,所以只需要注册ApplicationContext中提供的getBean方法即可,然后初始化multipartResolver中的multipartResolver变量 文件上传的配置 <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="102400000"></property></bean>2)初始化localeResolver 3) 4)初始化handlerMappings 5)初始化handlerAdapters 7.重要组件介绍 7.1 HandlerMapping介绍 1)BeanNameUrlHandlerMapping (默认) 2)SimpleUrlHandlerMapping 3)ControllerClassNameHandlerMapping 7.2 Controller介绍 1)ParameterizableViewController(参数控制器) 2)AbstractCommandController(命令控制器) * 需要继承AbstractCommandController类,并重写handle方法 * 通过构造方法注册命令类和命令名称,如: public MyCommandController(){
//注册命令类
this.setCommandClass(Person.class);
//命令名称
this.setCommandName("person");
} 3)FormController(表单控制器) * 需要继承SimpleFormController类,并重写doSubmitAction方法 * 通过构造方法注册命令类和命令名称,如: public MyFormController(){
this.setCommandClass(Person.class);
this.setCommandName("person");
} * 在spring配置文件中对表单控制器进行配置,如: <!-- 表单控制器 --> <bean id="myFormController" name="/form.action" class="cn.joker.controller.MyFormController"> <property name="successView" value="success"/>
<property name="formView" value="personForm"/>
</bean> 8 SpringMVC 基于注解开发 @Controller
@RequestMapping
请求方式
参数 传递
返回值 9.Spring 拦截器 实现HandlerInterceptor接口 preHandle:预处理回调方法,实现处理器的预处理(如登录检查),第三个参数为响应的处理器返回值:true表示继续流程(如调用下一个拦截器或处理器);false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应;
postHandle:后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null。 ? afterCompletion:整个请求处理完毕回调方法,即在视图渲染完毕时回调,如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中preHandle返回true的拦截器的afterCompletion。 |
7. [代码][Java]代码
1
|
springmvc的使用 |
8. [文件] SpringMVC使用示例.txt ~ 11KB 下载(32)
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
|
1.SpringMVC入门示例 1.1 web.xml配置文件 <!-- springmvc核心控制器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
1.2 springmvc-servlet.xml配置文件 <bean id="controlllerTest" name="/hello.do" class="cn.cunzhang.springmvc.ControllerTest"></bean>
<!-- springmvc配置文件 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
1.3 controller控制代码 public class ControllerTest extends AbstractController{ @Override
protected ModelAndView handleRequestInternal(HttpServletRequest arg0,
HttpServletResponse arg1) throws Exception {
System.out.println("Hello World");
return new ModelAndView("success");
}
} 请求为配置文件中的hello.do 请求路径为/jsp/success.jsp 字符串拼接的过程为prefix+ viewName + suffix 2.常用的handlerMapping 第一种 org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping 根据Bean中的name进行映射 配置文件:默认不配置 或者如下 <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean> <bean id="controlllerTest" name="/hello.do" class="cn.cunzhang.springmvc.ControllerTest"></bean> 通过bean中name为/hello.do映射而成 源码如下: protected String[] determineUrlsForHandler(String beanName)
{
List urls = new ArrayList();
if(beanName.startsWith("/"))
urls.add(beanName);
String aliases[] = getApplicationContext().getAliases(beanName);
String as[];
int j = (as = aliases).length;
for(int i = 0; i < j; i++)
{
String alias = as[i];
if(alias.startsWith("/"))
urls.add(alias);
}
return StringUtils.toStringArray(urls);
}
} 第二种 org.springframework.web.servlet.handler.SimpleUrlHandlerMapping 配置文件: <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props >
<prop key="/hello.do">controlllerTest</prop>
</props>
</property>
</bean>
<bean id="controlllerTest" class="cn.cunzhang.springmvc.ControllerTest"></bean> 通过key-value 映射获取路径 源码如下: 有一个方法为 setMappings,所以可以使用 mappings属性
protected void registerHandlers(Map urlMap)
throws BeansException
{
if(urlMap.isEmpty())
{
logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
} else
{
String url;
Object handler;
for(Iterator iterator = urlMap.entrySet().iterator(); iterator.hasNext(); registerHandler(url, handler))
{
java.util.Map.Entry entry = (java.util.Map.Entry)iterator.next();
url = (String)entry.getKey();
handler = entry.getValue();
if(!url.startsWith("/"))
url = (new StringBuilder("/")).append(url).toString();
if(handler instanceof String)
handler = ((String)handler).trim();
}
}
}
第三种 org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping 配置文件 <bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"></bean>
<bean class="cn.cunzhang.springmvc.TestController"></bean>
通过类名来访问 testControlller.do 类名首字母需要小写 源码如下: protected String[] generatePathMappings(Class beanClass) {
StringBuilder pathMapping = buildPathPrefix(beanClass);
String className = ClassUtils.getShortName(beanClass);
String path = (className.endsWith(CONTROLLER_SUFFIX) ?
className.substring(0, className.lastIndexOf(CONTROLLER_SUFFIX)) : className);
if (path.length() > 0) {
if (this.caseSensitive) {
pathMapping.append(path.substring(0, 1).toLowerCase()).append(path.substring(1));
}
else {
pathMapping.append(path.toLowerCase());
}
}
if (isMultiActionControllerType(beanClass)) {
return new String[] {pathMapping.toString(), pathMapping.toString() + "/*"};
}
else {
return new String[] {pathMapping.toString() + "*"};
}
}
3.常用的控制器 Controller 第一种 参数控制器 org.springframework.web.servlet.mvc.ParameterizableViewController 配置文件: <bean name="/tosuccess.do" class="org.springframework.web.servlet.mvc.ParameterizableViewController">
<property name="viewName" value="success"></property>
</bean>
源码如下: 配置viewName protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception
{
return new ModelAndView(getViewName());
}
第二种:命令控制器 org.springframework.web.servlet.mvc.AbstractCommandController.class 命令控制器的写法: 配置文件: <bean id="commonController" name="/tocommon.do" class="cn.cunzhang.springmvc.CommonController">
<property name="commandClass" value="cn.cunzhang.jbug.model.User"></property>
</bean>
CommonController 代码: public class CommonController extends AbstractCommandController{ @Override
protected ModelAndView handle(HttpServletRequest httpservletrequest,
HttpServletResponse httpservletresponse, Object obj,
BindException bindexception) throws Exception {
User user= (User) obj;
System.out.println(user);
return new ModelAndView("success");
}
} 请求参数:tocommon.do?userId=1&UserName=cunzhang 后台会获取到参数,不区分首字母大小写 User [userId=1, userName=cunzhang, userPassword=null, loginTime=null, logonTime=null] 第三种:表单控制器 org.springframework.web.servlet.mvc.SimpleFormController 配置文件: <bean id="formController" name="/form.do" class="cn.cunzhang.springmvc.FormController">
<property name="commandClass" value="cn.cunzhang.jbug.model.User"></property>
<property name="formView" value="form"></property>
<property name="successView" value="success"></property>
</bean>
表单 form.jsp <body> <form action="form.do" method="post"> id<input type="text" name="userId"></input> <br/> 姓名<input type="text" name="userName"></input> <br/> 密码<input type="password" name="userPassword"></input> <br/> <input type="submit" value="提交"></input> <br/> </form> </body> 代码: public class FormController extends SimpleFormController { @Override
protected void doSubmitAction(Object obj) throws Exception {
User user=(User) obj;
System.out.println(user);
super.doSubmitAction(obj);
}
} post方式可以提交,不支持get方式提交 4.注解开发 1.配置文件 <mvc:annotation-driven/> //可以去掉,因为下面已经包含了
<context:component-scan base-package="cn.cunzhang.springmvc"></context:component-scan>
<!-- springmvc配置文件 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
注解 @Controller 注解 @RequestMapping 代码一: 简单的请求,返回视图 import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class SimController { @RequestMapping("/hello.do")
public String hello(){
//返回的是viewName
return "success";
}
} 代码二: 接受请求,从HttpServletRequest请求中获取 @RequestMapping("/toUser.do")
public String toUser(HttpServletRequest request){
String name= request.getParameter("name");
System.out.println(name);
return "success";
}
代码三:直接获取参数列表 toUserName.do?userName=cunzhang 参数名必须匹配
@RequestMapping("/toUserName.do")
public String toUserName(String userName){
System.out.println(userName);
return "success";
}
代码四:多个参数传递 toUserall.do?userName=cunzhang&id=1&password=123 @RequestMapping("/toUserall.do")
public String toUserall(String userName,int id,String password){
System.out.println("用户名"+userName+" 用户id"+id +" 用户密码"+password);
return "success";
}
//
注意特殊类型*** 比如前台checkbox,接受方式使用 String[] checkbox
后台获取的checkbox为 A,B
//
注意特殊类型 Date解析
//日期类型格式装换器,会进行进制转换
@InitBinder
public void dateBinder(ServletRequestDataBinder binder){
binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"),true));
}
代码五:参数为对象 toUserobj.do?userName=cunzhang&userId=1&userPassword=123 @RequestMapping("/toUserobj.do")
public String toUserobj(User user){
System.out.println(user);
return "success";
}
注意: 如果出现多个类型 @RequestMapping("/toUserobj.do")
public String toUserobj(User user1,User user2){
System.out.println(user1);
System.out.println(user2);
return "success";
}
两者都能获取到参数 注意: 如果出现多个对象,但是对象中含有相同的属性 比如 User中含有name ,Person中含有name @RequestMapping("/toUserobj.do")
public String toUserobj(User user1,Person user2){
System.out.println(user1);
System.out.println(user2);
return "success";
}
两者都能获取到参数,即user1中有name值, user2中也会有name值 代码六 :数据提交和访问 @RequestMapping("/toUsermap.do")
public ModelAndView toUsermap(User user){
Map<String,Object> map=new HashMap<String,Object>();
//相当于 request.setAtribute("u",user);
map.put("u", user);
System.out.println(user);
return new ModelAndView("success",map);
}
返回值获取 EL表达式 ${u.userName} 代码七:同六 视图和对象分离 @RequestMapping("/toUserview.do")
public String toUserview(User user,Model model){
model.addAttribute("u", user);
return "success";
}
@RequestMapping("/toUserview.do")
public String toUserview(User user,Map<String,Object> map){
map.put("u",user);
return "success";
}
代码八:以流的形式使用ajax输出 @RequestMapping("/toAjax.do")
public void toAjax(User user,HttpServletResponse response){
String s="json字符串"+user;
try {
response.getWriter().write(s);
} catch (IOException e) {
e.printStackTrace();
}
}
@RequestMapping("/toAjaxpw.do")
public void toAjaxpw(User user,PrintWriter pw){
String s="json字符串"+user;
pw.write(s);
}
代码九:请求跳转 @RequestMapping("/redirectAction.do")
public String redirectAction(){
return "redirect:toAjax.do";
//路径 需要加上命名空间
}
|