1. 程式人生 > >反射應用進階篇之自定義反射工具類在springmvc中的應用

反射應用進階篇之自定義反射工具類在springmvc中的應用

本篇使用自定義工具類進行批量處理物件

---將批量源物件的屬性值注入到實際需要的目標類物件(屬性名相同,型別不同)中

專案使用maven構建war工程:  spring+spring MVC+Mybatis

回顧知識點:

事務:--->為什麼在使用AOP時需要使用spring-aspects 依賴(不匯入會報異常:切入點pointcut找不到依賴)

Aop是一種橫向抽取,簡單的這樣理解:比如當應用程式呼叫你業務邏輯元件(比如DAO)的方法時,該方法首先會被攔截,Aspect首先開始事物邊界,接著方法開始執行,
方法執行完後,Spring會提交事物,你的業務邏輯元件完全不知道自己處於事物管理中

AOP是面向切面的程式設計
在Spring裡,只有面向方法的切面。
比如你有一個方法A和方法B,你希望在A執行前執行方法B,但不能把B的程式碼貼到A中
因為和A類似的方法有很多,如果你全都貼過去的話,工作量大並且是重複的工作
這個時候你就定義了A為切面,B為處理程式。Spring 會在你呼叫A方法時自動執行B方法
實際過程是Spring 為含有A方法的類生成了一個代理類,該類也有一個叫A的方法但其實現卻是 B+A的實現
你呼叫A的時候,呼叫的物件是代理物件,但是你並不知道。
事務的程式設計正是上述情況的典型
事務處理程式碼是B(提交,回滾),事務執行程式碼是A(增刪改查)
在Spring 中已經內建了事務處理程式碼,也就是B,所以我們寫A方法,然後配置事務就可以了。

使用AOP而忘記匯入spring-aspects 依賴時會報下述異常:

 Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManager' defined in file [G:\javaEEWeb_Eclipse\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\MyWebPro\WEB-INF\classes\applicationContext-tran.xml]: BeanPostProcessor before instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0':

Cannot create inner bean '(inner bean)#77799cbb' of type[org.springframework.aop.aspectj.AspectJExpressionPointcut] while setting bean property 'pointcut'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#77799cbb': Failed to introspect bean class [org.springframework.aop.aspectj.AspectJExpressionPointcut] for lookup method metadata: could not find class that it depends on; nested exception isjava.lang.NoClassDefFoundError: org/aspectj/weaver/reflect/ReflectionWorld$ReflectionWorldException
   

---------------------------------------------------------------------------------------

實際開發中資料庫中的表資料往往和實際表示層頁面中的資料不太相同,比如下面這張申請貸款的使用者表,

id : 使用者id

username:  使用者名稱

submitDate: 提交日期

certiType: 申請貸款的證件型別,表中存的是0,1,2     0 身份證,1 護照, 2 其他

result: 稽核結果, 表中存的是0或1    0 未通過, 1通過

vid:  稽核人id 表中存的是稽核人id (關聯表)  ,而我們需要在頁面中展示的是稽核人姓名

效果預覽:

資料庫中資料:


而在頁面中展示的是;


---------------------------------------------------------------------------------------------

專案結構:


資料庫中:

兩張表


user表:


verifier表:


兩張表有關聯關係. user表通過外來鍵vid和verifier進行關聯.

web工程中:

pom.xml中需要匯入的依賴:


pojo之User:

package com.qx.pojo;

import java.util.Date;

public class User {
	private Integer id;        //id
	private String username;   //使用者名稱
	private Date submitDate;   //提交日期
	private String certiType;  //證件型別 //資料庫中存的是字串0,2,3
	private int result;        //稽核結果  //資料庫中存的是Integer型別 0或1
	private Integer vid;       //稽核人id

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public Date getSubmitDate() {
		return submitDate;
	}

	public void setSubmitDate(Date submitDate) {
		this.submitDate = submitDate;
	}

	public String getCertiType() {
		return certiType;
	}

	public void setCertiType(String certiType) {
		this.certiType = certiType;
	}

	public int getResult() {
		return result;
	}

	public void setResult(int result) {
		this.result = result;
	}

	public Integer getVid() {
		return vid;
	}

	public void setVid(Integer vid) {
		this.vid = vid;
	}
}

pojo之Verifier
package com.qx.pojo;

public class Verifier {

	private Integer vid;
	private String vname;
	
	public Integer getVid() {
		return vid;
	}
	public void setVid(Integer vid) {
		this.vid = vid;
	}
	public String getVname() {
		return vname;
	}
	public void setVname(String vname) {
		this.vname = vname;
	}
	
}


配置檔案之 db.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///mywebpro?characterEncoding=utf-8
jdbc.user=root
jdbc.password=root

配置檔案之mybatis.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
			<plugins>
			<!-- 
			3.4.2版本pagehelper
			<plugin interceptor="com.github.pagehelper.PageHelper">
				<property name="dialect" value="mysql"/>
			</plugin>
			 -->
			 <!--5.0版本pagehelper -->
			<plugin interceptor="com.github.pagehelper.PageInterceptor">
				<property name="helperDialect" value="mysql"/>
			</plugin>
			
	</plugins>
</configuration>


配置檔案之applicaitonContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

	<context:component-scan base-package="com.qx"></context:component-scan>
	
	<!-- 匯入配置檔案 -->
	<context:property-placeholder location="classpath:conf/db.properties"/>
	<!-- dataSource -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driver}"></property>
		<property name="jdbcUrl" value="${jdbc.url}"></property>
		<property name="user" value="${jdbc.user}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>
	<!-- 配置工廠 -->
	<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource"></property>
		<property name="configLocation" value="classpath:mybatis.xml"></property>
	</bean>
	<!-- 配置掃描mapper檔案 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="com.qx.mapper"></property>
	</bean>
</beans>

配置檔案之applicationContext-tran.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

	<!-- 事務管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
		<!-- 約定優於編碼 設定事務詳情 -->
			<tx:method name="get*" read-only="true" propagation="SUPPORTS"/>
			<tx:method name="find*" read-only="true" propagation="SUPPORTS"/>
			<tx:method name="select*" read-only="true" propagation="SUPPORTS"/>
			<tx:method name="add*"/>
			<tx:method name="save*"/>
			<tx:method name="insert*"/>
			<tx:method name="delete*"/>
			<tx:method name="update*"/>
		</tx:attributes>
	</tx:advice>
	
	<aop:config>
		<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.qx..service..*.*(..))"/>
	</aop:config>
</beans>

配置檔案之 sping-MVC.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd">

	<!-- 雖在applicationContext.xml中配置過了,最好還是在此處再配一次,防止找不到 -->
	<context:component-scan base-package="com.qx"></context:component-scan>
	
	<!-- 不需要配置id,因為用不到 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<!-- <property name="prefix" value="/"></property> -->
		<property name="suffix" value=".jsp"></property>
	</bean>
	
	<!-- 配置resource標籤,此標籤內部的地址不會被攔截 -->
	<mvc:resources location="/css/" mapping="/css/**"></mvc:resources>
	<mvc:resources location="/js/" mapping="/js/**"></mvc:resources>
	<mvc:resources location="/images/" mapping="/images/**"></mvc:resources>
	
	<mvc:annotation-driven></mvc:annotation-driven>
</beans>

web.xml中的配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
  <display-name>MyWebPro</display-name>
  
  <!-- needed for ContextLoaderListener -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:app*.xml</param-value>
	</context-param>

	<!-- Bootstraps the root web application context before servlet initialization -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	
	<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
	<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:spring-MVC.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<!-- Map all requests to the DispatcherServlet for handling -->
	<servlet-mapping>
		<servlet-name>springMVC</servlet-name>
		<url-pattern>*.action</url-pattern>
	</servlet-mapping>
</web-app>


utils之UserMutate

package com.qx.utils;

public class UserMutate {
	private Integer id;  //id
	private String username; //使用者名稱
	
	/*需轉換*/
	private String submitDate; //提交日期
	
	/*需轉換 "0"->身份證, "1"->護照, "2"->其他*/
	private String certiType; //證件型別
	
	/*需轉換 0->未通過, 1->通過 */
	private String result;  //稽核結果
	
	/*需要轉換 稽核人id-->稽核人name*/
	private String vid;  //稽核人id

	//getter and setter 方法  (必須要有)

	@Override
	public String toString() {
		return "UserMutate [id=" + id + ", username=" + username + ", submitDate=" + submitDate + ", certiType="
				+ certiType + ", result=" + result + ", vid=" + vid + "]";
	}
}


utils之自定義轉換器介面MyConvert

package com.qx.utils;


public interface MyConvert<S,T> {
	public T convert(S source);
}


utils之自定義操作bean的工具類MyBeanUtils(採用了反射技術)(重點)

package com.qx.utils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MyBeanUtils {
	
	private static Map<String, MyConvert> convertMap=new HashMap<>();
	
	// 參1:變數名  參2:給變數名對應的轉換器
	/**
	 * 
	 * @param name  變數名(即 key)  the name must be as same as the propertyName
	 * @param myConvert  該變數名對應的轉換器(即value)
	 */
	public static void addConvert(String name,MyConvert myConvert) {
		convertMap.put(name, myConvert);
	};
	
	/**
	 * 轉換批量物件
	 * @param sources  轉換前的原始資料
	 * @param tClass  轉換後的目表類
	 * @return
	 */
	public static <T> List<T> populateBean(List sources,Class<T> tClass) throws Exception{
		
		List<T> list=new ArrayList<>();//建立存放目標的集合
		
		//中間轉換資料
		for (int i = 0; i < sources.size(); i++) {//遍歷轉換前的原始資料
			Object o = sources.get(i);
			T newInstance = tClass.newInstance();//轉換後的用於存放資料的物件
			Field[] declaredFields = tClass.getDeclaredFields();//從目標中取出所有的欄位
			for (Field field : declaredFields) { //遍歷欄位
				String name = field.getName(); //找到每個欄位的名字,這個剛好在原始內容中都存在
				/*在原始的類中查詢當前同名的屬性,o.getclass()得到的是原始型別 不是Object.class*/
				PropertyDescriptor propertyDescriptor=new PropertyDescriptor(name, o.getClass());
				if (propertyDescriptor!=null) {
					Method readMethod = propertyDescriptor.getReadMethod();//獲取get方法
					Object invoke = readMethod.invoke(o);//呼叫get方法從當前正在需要轉換的物件上面獲取值
					
					MyConvert myConvert = convertMap.get(name);//根據當前的欄位名去轉換器中查詢是否有轉換器
					if (myConvert!=null) {
						invoke=myConvert.convert(invoke);
					}
					
					//field.setAccessible(true);//首先設定允許訪問私有變數
					//field.set(newInstance, invoke);//賦值
					PropertyDescriptor targetPro=new PropertyDescriptor(name, tClass);//從目標類上面找這個屬性,這裡建議使用set放個賦值
					if (targetPro!=null) {
						Method writeMethod = targetPro.getWriteMethod();//獲取set方法
						writeMethod.invoke(newInstance, invoke);//為目標型別物件的屬性賦值
					}
				}
			}
			list.add(newInstance);
		}
		//convertMap.clear();//注意 轉換器應該在所有的資料遍歷完成後清除(若多使用者共享該map則不能清除)
		return list;
		
	}
}

mapper之UserMapper介面
package com.qx.mapper;

import java.util.List;

import com.qx.pojo.User;

public interface UserMapper {

	// 查詢所有
	public List<User> findAll();
	
}

mapper之UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.qx.mapper.UserMapper">
	
	<select id="findAll" parameterType="com.qx.pojo.User" resultType="com.qx.pojo.User">
		select * from user;
	</select>
	
</mapper>

mapper之VerfierMapper介面
package com.qx.mapper;

public interface VerifierMapper {

	//根據稽核人id查詢稽核人名字
	public String findVerifierNameById(Integer id);
}

mapper之VerfierMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.qx.mapper.VerifierMapper">
	<select id="findVerifierNameById" parameterType="integer" resultType="string">
		select vname from verifier where vid=#{vid}
	</select>
</mapper>


業務層之UserService介面:

package com.qx.service;

import java.util.List;

import com.qx.utils.UserMutate;

public interface UserService {

	public List<UserMutate> findAll();
}

業務層之UserServiceImpl:(重點)
package com.qx.service;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.qx.mapper.UserMapper;
import com.qx.mapper.VerifierMapper;
import com.qx.pojo.User;
import com.qx.utils.MyBeanUtils;
import com.qx.utils.MyConvert;
import com.qx.utils.UserMutate;

@Service
public class UserServiceImpl implements UserService{

	@Autowired
	private UserMapper userMapper;
	
	@Autowired
	private VerifierMapper verifierMapper;
	
	@Override
	public List<UserMutate> findAll() {
		//從資料庫中查詢出所有使用者
		List<User> users = userMapper.findAll();
		//設定轉換器
		//Date--->String
		MyConvert<Date, String> submitDateConvert=new MyConvert<Date, String>() {

			@Override
			public String convert(Date source) {
				// TODO Auto-generated method stub
				if (source!=null) {  //此處可不判空,因為資料庫中要求,此項非空,對於沒要求非空的必須判空
					SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd");
					return simpleDateFormat.format(source);
				}
				return "";
			}
		};
		
		//String--->String 即 0-->身份證,1-->護照,2-->其他
		MyConvert<String, String> certiTypeConvert=new MyConvert<String, String>() {
			
			@Override
			public String convert(String source) {
				// TODO Auto-generated method stub
				switch (source) {
				case "0":
					return "身份證";
				case "1":
					return "護照";
				case "2":
					return "其他";
				default:
					break;
				}
				return "";
			}
		};
		
		//Integer--->String 即 0-->未通過, 1-->通過
		MyConvert<Integer, String> resultConvert=new MyConvert<Integer, String>() {
			
			@Override
			public String convert(Integer source) {
				// TODO Auto-generated method stub
				switch (source) {
				case 0:
					return "未通過";
				case 1:
					return "通過";
				default:
					break;
				}
				return "";
			}
		};
		
		//Integer--->String 即 稽核人id--->稽核人姓名
		MyConvert<Integer, String> vidConvert=new MyConvert<Integer, String>() {
			
			@Override
			public String convert(Integer source) {
				// TODO Auto-generated method stub
				String name = verifierMapper.findVerifierNameById(source);
				return name;
			}
		};
		
		//必須以屬性名作為key
		MyBeanUtils.addConvert("submitDate",submitDateConvert);
		MyBeanUtils.addConvert("certiType", certiTypeConvert);
		MyBeanUtils.addConvert("result", resultConvert);
		MyBeanUtils.addConvert("vid", vidConvert);
		
		try {
			//批處理
			List<UserMutate> userMutates=MyBeanUtils.populateBean(users, UserMutate.class);
			System.out.println(userMutates);
			return userMutates;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

}


控制層之UserController:

package com.qx.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import com.qx.service.UserService;
import com.qx.utils.UserMutate;

@Controller
@RequestMapping("/user")
public class UserController {

	@Autowired
	private UserService userService;
	
	@RequestMapping("/findAll")
	public ModelAndView findAllUser(){
		ModelAndView mv=new ModelAndView();
		
		List<UserMutate> userMutates = userService.findAll();
		mv.addObject("usermutates", userMutates);
		mv.setViewName("/userlist");
		return mv;
		
	}
}


檢視層之index.jsp:
<body>
	<jsp:forward page="/user/findAll.action"></jsp:forward>
</body>

檢視層之userlist.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core"  prefix="c"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<table border="1" width="100%">
		<tr>
			<td>使用者名稱</td>
			<td>提交日期</td>
			<td>證件型別</td>
			<td>稽核結果</td>
			<td>稽核人</td>
		</tr>
		<c:forEach items="${usermutates }"  var="usermutate">
			<tr>
				<td>${usermutate.username }</td>
				<td>${usermutate.submitDate }</td>
				<td>${usermutate.certiType }</td>
				<td>${usermutate.result }</td>
				<td>${usermutate.vid }</td>
			</tr>
		</c:forEach>
	</table>
</body>
</html>


啟動tomcat, 瀏覽器中輸入請求地址  localhost:8080/MyWebPro  回車即可看到展示的資料



專案下載地址: