1 面象切面编程-代理的jdk版实现
需求:
所有的对数据库有影响的操作,都要记日志
所有的查询的类的方法,都要计算执行时间
没有权限,就不能执行任何方法
横切性关注点 :需要关注的点称为 横切性关注点
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
参数:
loader - 定义代理类的类加载器
interfaces - 代理类要实现的接口列表
h - 指派方法调用的调用处理程序
返回值:
一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口
例子:没有权限,就不能执行任何方法
== UserInfo
public class UserInfo {
private int id;
private String userName;
private String password;
private String note;
}
== IUserDao
public interface IUserDao {
String addUser();
void delUser();
void updateUser();
void searchUser();
}
== UserDaoImpl
public class UserDaoImpl implements IUserDao{
public UserInfo user; //这里假设这个 user 如果不为null,则表示有权限
public String addUser() {
System.out.println("addUser方法执行了");
return "addUserxxx";
}
public void delUser() {
System.out.println("delUser方法执行了");
}
public void updateUser() {
System.out.println("updateUser方法执行了");
}
public void searchUser() {
System.out.println("searchUser方法执行了");
}
}
== ProxyFactory 代理工厂,用来创建代理对象
public class ProxyFactory implements InvocationHandler{
private Object targetObj; //代理对象
//返回值:创建好的代理对象
//loader 目标对象的类装载器
//interfaces 目标对象所实现的接口
//h 是一个实现了 InvocationHandler 的一个类
public Object createProxyInstance(Object targetObj){
this.targetObj = targetObj;
return Proxy.newProxyInstance(this.targetObj.getClass().getClassLoader(), this.targetObj.getClass().getInterfaces(),this);
}
//InvocationHandler 接口中声明的方法
//Object 代表被拦到的方法执行后的返回值
//method 代表当前被拦载到的方法
//args 代表这个方法的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null; //定义方法的返回值
UserDaoImpl impl = (UserDaoImpl)this.targetObj;
if(impl.user != null){ //有权限
result = method.invoke(this.targetObj, args); //调用了invoke这个方法之后,才能执行目标对象的方法
String methodName = method.getName(); //获得执行dao中方法的名称
if(methodName.contains("add") || methodName.contains("update") ||methodName.contains("del")){
//记录日志
try{
BufferedWriter bw=new BufferedWriter(new FileWriter("log.txt",true));
bw.write(new Date()+":"+methodName+" 被执行了");
bw.newLine();
bw.close();
}
catch(Exception ex){
ex.printStackTrace();
}
}
}else{
System.out.println("没有权限,不能执行");
}
return result;
}
}
== Test1:
public class Test1 {
public static void main(String[] args) {
UserDaoImpl userDao = new UserDaoImpl();
userDao.user = new UserInfo();
ProxyFactory factory=new ProxyFactory();
IUserDao dao = (IUserDao)factory.createProxyInstance(userDao);
//UserDaoImpl dao=(UserDaoImpl)factory.createProxyInstance(userDao); 报错 com.sun.proxy.$Proxy0 cannot be cast to cat.dao.UserDaoImpl
dao.addUser();
dao.delUser();
System.out.println("---------");
}
}
2 使用 Cglib 生成代理
1 导包 cglib-nodep-2.2.jar // 这个Spring核心库中有
Enhancer允许为非接口类型创建一个Java代理。
Enhancer动态创建了给定类型的子类但是拦截了所有的方法。
和Proxy不一样的是,不管是接口还是类他都能正常工作。
//代理工厂
public class CGlibProxyFactory implements MethodInterceptor {
public Object targetobj ; //要代理一个目标对象
public Object createProxInstance(Object targetobj){
this.targetobj = targetobj;
Enhancer enhancer=new Enhancer(); //用 cglib 提供的api创建目标对象
enhancer.setSuperclass(this.targetobj.getClass()); // 设置代理目标
enhancer.setCallback(this); //设置回调函数
return enhancer.create(); //创建代理对象
}
//Object 代表 被拦到的方法执行后的返回值
//method 代表被拦到的方法
//args 代表被拦到的方法的参数
//methodProxy 代表被拦到的方法的代理对象
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
Object result = null;
UserDaoImpl userDao = (UserDaoImpl)this.targetobj;
if(userDao.user != null){
result = methodProxy.invoke(this.targetobj, args);
}else{
System.out.println("CGLIB: 方法 " + method.getName() + " 因为没有权限,没有执行 ");
}
return result;
}
}
对于本例,如果目标对象没有实现接口,也能创建代理对象
public static void main(String[] args) {
UserDaoImpl userDao = new UserDaoImpl();
//userDao.user = new UserInfo();
CGlibProxyFactory factory = new CGlibProxyFactory();
//IUserDao dao = (IUserDao) factory.createProxInstance(userDao);
UserDaoImpl dao = (UserDaoImpl) factory.createProxInstance(userDao);
dao.addUser();
//dao.delUser();
System.out.println("---------");
}
实际上, 在Spring中 ,有两种方式创建代理对象, 一种用JDK 的API ,另一种就是cglib的方式
如果目标对象实现了接口, 它就用第一方式,如果没有实现接口就用第二种方式
3 aop编程的一些概念
=== Aspect 切面
横切性关注点的抽象
=== jointpoint 连接点
指被拦到的点 ,在spring中指的就是被拦到的方法 ,比如 addUser() ,delUser() 等方法
=== Pointcut 切入点
指的是对哪些连接点进行拦载 ,比如,所有的以add开头的方法
=== Advice 通知
拦载到 jointpoint 要做的事情,有前置通知,后置通知,例外通知,最终通知,环绕通知
=== target 被代理的目标对象
=== weave 织入
将切面(Aspect) 应用到代理对象的过程 ,经过织入,代理对象就产生了
===Introduction 引入
在不改类的代码的前提下, Introduction 可以在对象的运行期,动态的给对象添加一些功能,
实际上,这些功能并不是真的添到了被代理的目标对象上,而是添到了代理对象上,给用户的感觉
就好象是委托对象,原来就有这些功能一样
4 使用 Spring 创建代理对象(注解方式)
接口
public interface IUserDao {
void addUser();
void delUser();
void updateUser();
void searchUser();
}
实现类
@Repository
public class UserDaoImpl implements IUserDao {
public UserInfo user;
public void addUser() {
int a=9/0;
System.out.println("addUser方法执行了");
}
public void delUser() {
System.out.println("delUser方法执行了");
}
public void updateUser() {
System.out.println("updateUser方法执行了");
}
public void searchUser() {
System.out.println("searchUser方法执行了");
}
}
1) 导包
spring-aop-3.2.4.RELEASE.jar
com.springsource.org.aopalliance-1.0.0.jar
aspectjlib.jar
aspectjrt.jar
aspectjweaver-1.6.9.jar 必用
2) 引入名称空间
xmlns:aop="http://www.springframework.org/schema/aop"
3) 加入配置节
<beans ....>
<aop:aspectj-autoproxy />
</beans>
4) 创建切面
@Aspect @Component
public class MyAspect {
@Pointcut("execution(* com.neusoft.dao.UserDaoImpl.*(..))") //切入点表达式
private void anyMethodXXX(){} //空的,方法名自定义 ,
@Before("anyMethodXXX()") //声明了一个前置通知,一定要注意,里面的() 不能少
public void beforeMethod(){
// int a = 10/0; //测试例外通知的
System.out.println("前置通知被触发了");
}
@AfterReturning("anyMethodXXX()") //后置通知
public void afterMethod(){
System.out.println("后置通知被触发了");
}
@After("anyMethodXXX()") //最终通知
public void finallyMethod(){
System.out.println("最终通知被触发了");
}
@AfterThrowing("anyMethodXXX()") //例外通知
public void execptionMethod(){
System.out.println("例外通知被触发了");
}
}
5) 测试:
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
IUserDao dao = (IUserDao) ctx.getBean("userDaoImpl"); //不能返回UserDaompl ,必须是接口类型
dao.addUser();
}
说明 @Pointcut("execution(* com.neusoft.dao.UserDaoImpl.*(..))")
execution() // 表示拦截正在执行的方法
* //表示不管这个方法的返回值是什么
com.neusoft.dao.UserDaoImpl //表示拦载的类名
.* //表示不管方法名叫什么
(..) //表示不管方法的参数是什么
例
@Pointcut("execution(* com.neusoft.dao.UserDaoImpl.add*(..))||" 过滤所有add打头的方法
+ "execution(* com.neusoft.dao.UserDaoImpl.upd*(..))||" 地滤所有的upd打头的方法
例子:通过 JoinPoint 连接点 ,获取更多信息
== IUserDao:(添加)
public interface IUserDao {
String addUser(String p1,int p2);
//delUser的方法也重载有两个参数
...
}
== UserDaoImpl:
public class UserDaoImpl implements IUserDao{
@Override
public String addUser(String p1, int p2) {
System.out.println("带参数的 addUser 方法执行了");
return "张飞";
}
//delUser的方法也重载有两个参数
public void test(){
System.out.println("测试方法....");
}
}
== MyAspect:
@Before("anyMethodXXX()")
public void beforeMethod(JoinPoint point){
UserDaoImpl userDao = (UserDaoImpl) point.getTarget(); //获得目标对象
userDao.test(); //访问目标对象
//取得方法的所有参数
Object[] args = point.getArgs();
for(Object obj :args){
System.out.println(obj);
}
//取得方法的名称
//Signature getSignature() :获取连接点的方法签名对象
String methodName = point.getSignature().getName();
System.out.println(methodName);
System.out.println("前置通知被触发了");
}
== Test
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
IUserDao dao = (IUserDao) ctx.getBean("userDaoImpl");
dao.addUser("apple", 2);
dao.delUser("banana",5);
//运行后得到所有方法的参数
}
例子:方法的传递
@Before("anyMethodXXX()&&args(userName,password)")
public void beforeMethod(String userName,int password ){
System.out.println("前置通知被触发了,接到的参数是"+userName +" --- "+ password);
}
Test:
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
IUserDao dao = (IUserDao) ctx.getBean("userDaoImpl");
dao.addUser("apple", 2);
}
例子:方法的返回值 (在后置通知中)
@AfterReturning(pointcut="anyMethodXXX()",returning="resultXXX") //后置通知
public void afterMethod(String resultXXX){ //resultXXX 就是 UserDaoImpl 中的方法的 返回值
System.out.println("方法之后的返回值是:" + resultXXX);
System.out.println("后置通知被触发了");
}
Test:
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
IUserDao dao = (IUserDao) ctx.getBean("userDaoImpl");
dao.addUser("apple", 2); //有返回值 :张飞
System.out.println("-----------------");
dao.delUser(); //返回值为 null
例子:取异常信息
@AfterThrowing(pointcut="anyMethodXXX()",throwing="ex") //例外通知
public void execptionMethod(Exception ex){
System.out.println("异常信息是:" + ex.getMessage()); //异常信息取出来,程序是异常停止的
System.out.println("例外通知被触发了");
}
环绕通知:
将上面的通知都删掉
@Aspect @Component
public class MyAspect {
@Pointcut("execution (* com.neusoft.dao.UserDaoImpl.*(..))")
private void anyMethodXXX(){}
@Around("anyMethodXXX()") //环绕通知
public Object aroundMethod(ProceedingJoinPoint point){
Object result = null;
System.out.println("前置通知");
try {
//result=point.proceed(); 这个result就是目标方法的返回值
point.proceed();
} catch (Throwable ex) {
System.out.println("异常信息:"+ex.getMessage());
System.out.println("例外通知");
}finally{
System.out.println("最终通知");
}
System.out.println("后置通知");
return result;
}
}
Test:
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
IUserDao dao = (IUserDao) ctx.getBean("userDaoImpl");
System.out.println(dao.addUser("zzz",5)+"**");
System.out.println("-----------------");
dao.delUser();
}
5 使用 Spring 创建代理对象(基于XML配置的方式)
要被代理的类不变
== 切面
@Component
public class MyAspectNew {
public void beforeMethod(){
System.out.println("前置通知被触发了");
}
public void afterMethod(){
System.out.println("后置通知被触发了");
}
public void finallyMethod(){
System.out.println("最终通知被触发了");
}
public void execptionMethod(){
System.out.println("例外通知被触发了");
}
public Object aroundMethod(ProceedingJoinPoint point){
Object result=null;
System.out.println("前置通知");
try{
point.proceed() ; //让被拦到的方法往下执行,如果目标方法后面还有切面,就先执行切面,如果没有切面了,就直接执行目标方法
}
catch(Throwable ex){
System.out.println("异常信息:"+ex.getMessage());
System.out.println("例外通知");
}
finally{
System.out.println("最终通知");
}
System.out.println("后置通知");
return result;
}
}
==配置文件 ===
<context:component-scan base-package="com" />
<aop:config>
<aop:aspect ref="myAspectNew">
<aop:pointcut id="BBB" expression="execution(* com.dao.UserDaoImpl.*(..))"/>
<aop:before pointcut-ref="BBB" method="beforeMethod" />
<aop:after-returning pointcut-ref="BBB" method="afterMethod"/>
<aop:after-throwing pointcut-ref="BBB" method="execptionMethod"/>
<aop:after pointcut-ref="BBB" method="finallyMethod"/>
<aop:around pointcut-ref="BBB" method="aroundMethod"/>
</aop:aspect>
</aop:config>
6 Spring JDBC
导入mysql 的驱动包 mysql-connector-java-5.1.7-bin.jar
导入 dbcp 等等的jar包( 添加 spring 库 Presistance )
1) 配置数据源
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <!-- 单实例bean -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="initialSize" value="10"/> <!-- 连接池启动时的初始值 -->
<property name="maxActive" value="50"/> <!-- 连接池的最大值 -->
<property name="maxIdle" value="5"/> <!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->
<property name="minIdle" value="3"/> <!-- 最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请 -->
</bean>
2) 开启自动扫描
<context:component-scan base-package="com" />
3) 数据访问层
JdbcTemplate 类: 资料:https://blog.csdn.net/guochunyang/article/details/50442305
update 方法 - 用于执行DML 语句:insert delete update;执行成功返回数据库当中受影响的记录条数,失败则返回-1
queryForObject() 方法 - 用于查询返回唯一结果的情况
public <T> T queryForObject(String sql, Object[] args, RowMapper<T> rowMapper) throws DataAccessException {
List<T> results = query(sql, args, new RowMapperResultSetExtractor<T>(rowMapper, 1));
return DataAccessUtils.requiredSingleResult(results);
}
queryForObject此重装第2个参数Class<T> requiredType只能是基本数据类型的封装类,如Integer、String
BeanPropertyRowMapper:
它可自动将一行数据映射到指定类的实例中 它首先将这个类实例化,然后通过名称匹配的方式,映射到属性中去。
例如:属性名称(vehicleNo)匹配到同名列或带下划线的同名列(VEHICLE_NO)
spring 提供框架的同时还提供了一种规范,包括命名规范,自动转换就会要求你javabean的成员变量命名符合规则,
匹配不成功就变成null。
BeanPropertyRowMapper是根据字段名和实体类中的标准Setter方法进行映射的。
代码:UserDaoImpl可以不再实现接口
@Repository
public class UserDaoImpl {
private JdbcTemplate t; // 依赖对象 (由spring提供)
@Resource // 与配置文件中id="dataSource"刚好对应 )
public void setDataSource(DataSource dataSource) {
t = new JdbcTemplate(dataSource);
}
//增加
public int addUser(UserInfo user){
String sql="insert into userinfo (userName,password,note) values(?,?,?) ";
Object[] paramList = {
user.getUserName(),
user.getPassword(),
user.getNote()
};
return t.update(sql,paramList);
}
//删除
public int delUser(int id){
String sql="delete from userinfo where id=? ";
return t.update(sql,id);
}
//修改
public int updateUser(UserInfo user){
String sql="update userInfo set userName=?,password=?, note=? where id=? ";
Object[] paramList = {
user.getUserName(),
user.getPassword(),
user.getNote(),
user.getId()
};
return t.update(sql,paramList);
}
//根据id查询用户
public UserInfo getUserById(int id){
String sql = "select * from userInfo where id=?"; // 注意:如果查询结果多于一条,将报异常
Object[] paramList = { id };
Object user = t.queryForObject(sql, paramList, new BeanPropertyRowMapper(UserInfo.class));
return (UserInfo)user;
}
//查询所有用户
public List<UserInfo> getUserList(){
String sql = "select * from userInfo ";
return (List<UserInfo>)t.query(sql, new BeanPropertyRowMapper(UserInfo.class));
}
//查询单个int值
public int getUserCount(){
String sql = "select count(*) from userInfo";
return t.queryForInt(sql);
}
//查询单个字段
public String getUserName(int id){
String sql="select userName from userinfo where id = " + id;
return (String) t.queryForObject(sql, String.class);
}
// 返回map
public Map getUserMapData(int id) {
String sql = "select * from userInfo where id=" + id;
return t.queryForMap(sql);
}
}
}
4) 测试Junit
public class UserDaoImplTest {
private UserDaoImpl dao ; //使用spring,拿到dao
@Before
public void setUp() throws Exception {
ClassPathXmlApplicationContext ctx= new ClassPathXmlApplicationContext("beans.xml");
dao = ctx.getBean("userDaoImpl",UserDaoImpl.class);
}
@Test
public void testAddUser() {
UserInfo user = new UserInfo();
user.setUserName("Tom");
user.setPassword("Tom");
dao.addUser(user);
System.out.println("OK");
}
@Test
public void testDelUser() {
fail("Not yet implemented");
}
@Test
public void testUpdateUser() {
fail("Not yet implemented");
}
@Test
public void testGetUserById() {
UserInfo user = dao.getUserById(19);
System.out.println(user);
}
@Test
public void testGetUserList() {
List<UserInfo> ul = dao.getUserList();
for(UserInfo u : ul){
System.out.println(u);
}
}
@Test
public void testGetUserCount() {
fail("Not yet implemented");
}
@Test
public void testGetUserName() {
fail("Not yet implemented");
}
@Test
public void testGetUserMapData() {
fail("Not yet implemented");
}
@Test
public void testAddUser2() {
fail("Not yet implemented");
}
}