Spring-03

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&amp;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");
				}
			
			}				
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值