使用"横切"技术,AOP把软件系统分为两个部分:核心关注点横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事务。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

AOP核心概念

1、横切关注点

对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点

2、切面(aspect)

类是对物体特征的抽象,切面就是对横切关注点的抽象

3、连接点(joinpoint)

被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器

4、切入点(pointcut)

对连接点进行拦截的定义

5、通知(advice)

所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类

6、目标对象

代理的目标对象

7、织入(weave)

将切面应用到目标对象并导致代理对象创建的过程

8、引入(introduction)

在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段

使用ProxyFactoryBean实现AOP

Spring自己的AOP实现在于ProxyFactoryBean。

接口

package com.shoudongdaili;

public interface IPerson {

    void say();

}

实现类

package com.shoudongdaili;

public class Person3 implements IPerson {

    private String name3;
private int age3; public String getName3() {
return name3;
}
public void setName3(String name3) {
this.name3 = name3;
}
public int getAge3() {
return age3;
}
public void setAge3(int age3) {
this.age3 = age3;
}
@Override
public String toString() {
return "Person3 [name3=" + name3 + ", age3=" + age3 + "]";
} @Override
public void say() {
System.out.println(toString());
} }

通知类

package com.shoudongdaili;

import java.lang.reflect.Method;

import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; public class MyAdvice3 implements MethodBeforeAdvice { @Override
public void before(Method method, Object[] args, Object target) throws Throwable {
//arg0 是 目标类的方法 arg1是目标类的入参数 arg2是目标类实例 发生异常则抛给Throwable
System.out.println("before my advice3...");
} }

bean.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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
   <!-- 代理前原对象 -->
<bean id="person3" class="com.shoudongdaili.Person3"></bean>

  <!-- 通知类 -->
<bean id="myAdvice3" class="com.shoudongdaili.MyAdvice3"></bean>

  <!-- 代理对象 -->  
<bean id="proxyPerson3" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="com.shoudongdaili.IPerson"></property>
<property name="target" ref="person3"></property>
<property name="interceptorNames">
<list>
<value>myAdvice3</value>
</list>
</property>
</bean> </beans>

测试类

package com.shoudongdaili;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BeanTest3 {

    public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("zidongdaili/shoudongdaili-bean.xml");
// IPerson的实现类有Person3和proxyPerson3代理类这两个,注意这里是使用proxyPerson3
IPerson person3 = (IPerson) context.getBean("proxyPerson3");
person3.say();
} }

源代码解读

  然后我们就要源码分析下这一过程,先看下是如何产生代理对象的,在ProxyFactoryBean的getObject方法中:

public class ProxyFactoryBean extends ProxyCreatorSupport
implements FactoryBean<Object>, BeanClassLoaderAware, BeanFactoryAware {
@Override
public Object getObject() throws BeansException {
   //重点一
initializeAdvisorChain();
if (isSingleton()) {
  //重点二
return getSingletonInstance();
}
else {
if (this.targetName == null) {
logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
"Enable prototype proxies by setting the 'targetName' property.");
}
return newPrototypeInstance();
}
}
}

  重点1:就是根据我们配置的interceptorNames来获取对应的bean,并却转化成Advisor。

  this.advisorChainInitialized:标示是否已进行过初始化,若以初始化则不再进行初始化。然后就是将interceptorNames转化成Advisor。根据interceptorNames所包含的字符串到容器中进行查找,如果含有*则,则表示进行一定的匹配,符合的都会纳入。

  如下:

    private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
if (this.advisorChainInitialized) {
return;
} if (!ObjectUtils.isEmpty(this.interceptorNames)) {
if (this.beanFactory == null) {
throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
"- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
} // Globals can't be last unless we specified a targetSource using the property...
if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX) &&
this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) {
throw new AopConfigException("Target required after globals");
} // Materialize interceptor chain from bean names.
for (String name : this.interceptorNames) {
if (logger.isTraceEnabled()) {
logger.trace("Configuring advisor or advice '" + name + "'");
} if (name.endsWith(GLOBAL_SUFFIX)) {
if (!(this.beanFactory instanceof ListableBeanFactory)) {
throw new AopConfigException(
"Can only use global advisors or interceptors with a ListableBeanFactory");
}
addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
} else {
// If we get here, we need to add a named interceptor.
// We must check if it's a singleton or prototype.
Object advice;
if (this.singleton || this.beanFactory.isSingleton(name)) {
// Add the real Advisor/Advice to the chain.
advice = this.beanFactory.getBean(name);
}
else {
// It's a prototype Advice or Advisor: replace with a prototype.
// Avoid unnecessary creation of prototype bean just for advisor chain initialization.
advice = new PrototypePlaceholderAdvisor(name);
}
addAdvisorOnChainCreation(advice, name);
}
}
} this.advisorChainInitialized = true;
}

  这中间页经过了Advice到Advisor的转换,如下:

    private void addAdvisorOnChainCreation(Object next, String name) {
// We need to convert to an Advisor if necessary so that our source reference
// matches what we find from superclass interceptors.
Advisor advisor = namedBeanToAdvisor(next);
if (logger.isTraceEnabled()) {
logger.trace("Adding advisor with name '" + name + "'");
}
addAdvisor(advisor);
}
    private Advisor namedBeanToAdvisor(Object next) {
try {
return this.advisorAdapterRegistry.wrap(next);
}
catch (UnknownAdviceTypeException ex) {
// We expected this to be an Advisor or Advice,
// but it wasn't. This is a configuration error.
throw new AopConfigException("Unknown advisor type " + next.getClass() +
"; Can only include Advisor or Advice type beans in interceptorNames chain except for last entry," +
"which may also be target or TargetSource", ex);
}
}
    public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
if (adviceObject instanceof Advisor) {
return (Advisor) adviceObject;
}
if (!(adviceObject instanceof Advice)) {
throw new UnknownAdviceTypeException(adviceObject);
}
Advice advice = (Advice) adviceObject;
if (advice instanceof MethodInterceptor) {
// So well-known it doesn't even need an adapter.
return new DefaultPointcutAdvisor(advice);
}
for (AdvisorAdapter adapter : this.adapters) {
// Check that it is supported.
if (adapter.supportsAdvice(advice)) {
return new DefaultPointcutAdvisor(advice);
}
}
throw new UnknownAdviceTypeException(advice);
}
public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable {
public DefaultPointcutAdvisor(Advice advice) {
this(Pointcut.TRUE, advice);
}
}

这个包裹过程已经见过很多遍了,采用了适配器的模式。

之后又是和其他的AOP方式接轨了,设置一些列要实现的接口和参数,使用DefaultAopProxyFactory先创建出AopProxy,要么是JdkDynamicAopProxy,要么是CglibAopProxy,然后就可以调用AopProxy的getProxy方法来获取代理对象。

具体JdkDynamicAopProxy和CglibAopProxy的区别联系,参阅java中代理,静态代理,动态代理以及spring aop代理方式,实现原理统一汇总

这种方式实现的AOP还是比较麻烦的,同时配置一个ProxyFactoryBean仅能实现对一个目标对象的拦截,要想拦截多个目标对象,需要配置多个ProxyFactoryBean。所以大部分还是使用Spring引进的aspectj的AOP方式来进行AOP编程。

使用DefaultAdvisorAutoProxyCreator实现自动代理完成AOP

接口

package com.zidongdaili;

public interface IPerson4 {

    void sayhi();

}

实现类

package com.zidongdaili;

public class Person4 implements IPerson4 {

    private String name4;
private int age4; public String getName4() {
return name4;
}
public void setName4(String name4) {
this.name4 = name4;
}
public int getAge4() {
return age4;
}
public void setAge4(int age4) {
this.age4 = age4;
}
@Override
public String toString() {
return "Person4 [name4=" + name4 + ", age4=" + age4 + "]";
} @Override
public void sayhi() {
System.out.println(toString());
} }

通知类

package com.zidongdaili;

import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice; public class MyAdvice4 implements MethodBeforeAdvice { @Override
public void before(Method method, Object[] args, Object target) throws Throwable {
//arg0 是 目标类的方法 arg1是目标类的入参数 arg2是目标类实例 发生异常则抛给Throwable
System.out.println("before my advice4...");
} }

bean.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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="person4" class="com.zidongdaili.Person4"></bean> <bean id="myAdvice4" class="com.zidongdaili.MyAdvice4"></bean> <bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- id="advisor" 可以不写 -->
<property name="pattern">
<value>.*say.+</value> <!-- 业务实现方法名匹配 -->
</property>
<property name="advice">
<ref bean="myAdvice4" />
</property>
</bean>

  <!-- 自动代理 -->
<bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
</bean> </beans>

测试类

package com.zidongdaili;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BeanTest4 {

    public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("zidongdaili/zidongdaili-bean.xml"); IPerson4 person4 = (IPerson4) context.getBean("person4");
person4.sayhi(); } }

使用BeanNameAutoProxyCreator实现自动代理完成AOP

BeanNameAutoProxyCreator是自动代理创建器的三种(BeanNameAutoProxyCreator,DefaultAdvisorAutoProxyCreator,AbstractAdvisorAutoProxyCreator)之一.它是根据拦截器和设置的Bean的名称表达式做匹配来创建代理.下面是个例子

1.主要依赖(略)

2.声明一个环绕通知(拦截器)

public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println(getClass()+"调用方法前");
Object ret=invocation.proceed();
System.out.println(getClass()+"调用方法后");
return ret;
}
}

3.要创建代理的目标类与接口

public interface UserService {
void print();
}
public class UserServiceImpl implements UserService {
public void print(){
System.out.println(getClass()+"#print");
}
}

4.配置

@Configuration
public class AppConfig {
//要创建代理的目标Bean
@Bean
public UserService userService(){
return new UserServiceImpl();
}
//创建Advice或Advisor
@Bean
public Advice myMethodInterceptor(){
return new MyMethodInterceptor();
}
//使用BeanNameAutoProxyCreator来创建代理
@Bean
public BeanNameAutoProxyCreator beanNameAutoProxyCreator(){
BeanNameAutoProxyCreator beanNameAutoProxyCreator=new BeanNameAutoProxyCreator();
//设置要创建代理的那些Bean的名字
beanNameAutoProxyCreator.setBeanNames("userSer*");
//设置拦截链名字(这些拦截器是有先后顺序的)
beanNameAutoProxyCreator.setInterceptorNames("myMethodInterceptor");
return beanNameAutoProxyCreator;
}
}

5.测试

public class Main {
public static void main(String[] args) {
ApplicationContext applicationContext=new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService= applicationContext.getBean(UserService.class);
userService.print();
}
}

源码分析

BeanNameAutoProxyCreator是一个BeanPostProcessor.它在Bean实例化随后,调用回调org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization进行后期处理来完成代理的创建.
其中AbstractAutoProxyCreator是BeanNameAutoProxyCreator的超类,BeanNameAutoProxyCreator没有重写postProcessAfterInitialization方法.下面看看这个方法:

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
//关键代码在这里
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}

再看看wrapIfNecessary方法:

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (beanName != null && this.targetSourcedBeans.contains(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;
} //这个bean是否匹配要创建代理也是在这个方法.
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;
}

再看看createProxy方法:

protected Object createProxy(
Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
for (Advisor advisor : advisors) {
proxyFactory.addAdvisor(advisor);
}
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
//关键代码看这里
return proxyFactory.getProxy(getProxyClassLoader());
}

再看看org.springframework.aop.framework.ProxyFactory#getProxy(java.lang.ClassLoader)如下:

public Object getProxy(ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}

再看看org.springframework.aop.framework.ProxyCreatorSupport#createAopProxy

public Object getProxy(ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}

再看看createAopProxy方法

protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
return getAopProxyFactory().createAopProxy(this);
}

剩下的就与ProxyFactoryBean创建代理类似了.

手动实现自动代理实现AOP

我们也可以写一个类,来实现DefaultAdvisorAutoProxyCreator自动代理的功能!

首先,我们需要实现一个接口,也就是BeanPostProcessor接口。
BeanPostProcessor接口作用是:如果我们需要在Spring容器完成Bean的实例化、配置和其他的初始化前后添加一些自己的逻辑处理,我们就可以定义一个或者多个BeanPostProcessor接口的实现,然后注册到容器中。

而我们想要在原型对象bean被创建之后就代理了,就必须在原来的容器中拿到原来的原型对象,需要拿到原来spring容器中的切面对象,这个时候,我们就需要原来的容器,这个时候就需要另一个接口,也就是ApplicationContextAware接口!

通过这2个接口,我们就可以实现自动代理了。

package cn.hncu.xmlImpl;

import org.springframework.aop.Advisor;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; public class MyAutoProxy implements BeanPostProcessor,ApplicationContextAware{
private ApplicationContext applicationContext=null; //bean创建之前调用
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;//在这里,我们直接放行
} //bean创建之后调用
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
ProxyFactoryBean factory = new ProxyFactoryBean();
//把原型对象放入代理工厂
factory.setTarget(bean);
//在这里
Advisor adv = applicationContext.getBean(Advisor.class);
factory.addAdvisor(adv);
//返回被代理后的对象
return factory.getObject();
} //拿到原来的spring中的容器
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext=applicationContext;
} }

bean.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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="person4" class="com.zidongdaili.Person4"></bean> <bean id="myAdvice4" class="com.zidongdaili.MyAdvice4"></bean> <bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- id="advisor" 可以不写 -->
<property name="pattern">
<value>.*say.+</value> <!-- 业务实现方法名匹配 -->
</property>
<property name="advice">
<ref bean="myAdvice4" />
</property>
</bean> <!-- 自己写的自动代理 -->
<bean class="cn.hncu.xmlImpl.MyAutoProxy"></bean> </beans>

Spring里的aop实现方式和源码分析的更多相关文章

  1. Spring第四天,BeanPostProcessor源码分析,彻底搞懂IOC注入及注解优先级问题!

  2. Quartz学习--二 Hello Quartz! 和源码分析

    Quartz学习--二  Hello Quartz! 和源码分析 三.  Hello Quartz! 我会跟着 第一章 6.2 的图来 进行同步代码编写 简单入门示例: 创建一个新的java普通工程 ...

  3. Java并发编程(七)ConcurrentLinkedQueue的实现原理和源码分析

    相关文章 Java并发编程(一)线程定义.状态和属性 Java并发编程(二)同步 Java并发编程(三)volatile域 Java并发编程(四)Java内存模型 Java并发编程(五)Concurr ...

  4. Kubernetes Job Controller 原理和源码分析(一)

    概述什么是 JobJob 入门示例Job 的 specPod Template并发问题其他属性 概述 Job 是主要的 Kubernetes 原生 Workload 资源之一,是在 Kubernete ...

  5. Kubernetes Job Controller 原理和源码分析(二)

    概述程序入口Job controller 的创建Controller 对象NewController()podControlEventHandlerJob AddFunc DeleteFuncJob ...

  6. Kubernetes Job Controller 原理和源码分析(三)

    概述Job controller 的启动processNextWorkItem()核心调谐逻辑入口 - syncJob()Pod 数量管理 - manageJob()小结 概述 源码版本:kubern ...

  7. 涨姿势:Spring Boot 2.x 启动全过程源码分析

    目录 SpringApplication 实例 run 方法运行过程 总结 上篇<Spring Boot 2.x 启动全过程源码分析(一)入口类剖析>我们分析了 Spring Boot 入 ...

  8. Spring Boot 2.x 启动全过程源码分析

    Spring Boot 2.x 启动全过程源码分析 SpringApplication 实例 run 方法运行过程 上面分析了 SpringApplication 实例对象构造方法初始化过程,下面继续 ...

  9. Android Debuggerd 简要介绍和源码分析(转载)

    转载: http://dylangao.com/2014/05/16/android-debuggerd-%E7%AE%80%E8%A6%81%E4%BB%8B%E7%BB%8D%E5%92%8C%E ...

随机推荐

  1. B. Ohana Cleans Up(Codeforces Round #309 (Div. 2))

    B. Ohana Cleans Up   Ohana Matsumae is trying to clean a room, which is divided up into an n by n gr ...

  2. svn自助改动password(PHP脚本实现)

    #创建脚本文件夹 mkdir -p /var/www/svn/svntools #创建apache配置文件 touch /etc/httpd/conf.d/alias.conf #输入下面内容: Al ...

  3. 最新phpstudy2016安装教程及流程

    最新phpstudy2016安装教程及流程,帮助站长快速搭建网站服务器平台! phpstudy软件简介 该程序包集成最新的Apache+Nginx+LightTPD+PHP+MySQL+phpMyAd ...

  4. JavaScript 之 变量

    一:作用域 说起变量第一个要说到的肯定就是作用域,正是因为不熟悉JS的 作用域,往往就会把面向对象的作用域张冠李戴,毕竟有些东西总是习惯性的这样,但是并不是每次照搬都是可以的,那么下一个问题就来了,j ...

  5. MVC模式下值的传递

    公司项目转用MVC开发,故学习总结一下mvc会用到的常用传值方法:       正如大家都熟悉的,MVC路由及运行机制是:       首先,Web 浏览器向服务器发送一条URL 请求,如http:/ ...

  6. Android Service完全解析,关于服务你所需知道的一切(上)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/11952435 相信大多数朋友对Service这个名词都不会陌生,没错,一个老练的A ...

  7. 解决 在POM配置Maven plugin提示错误“Plugin execution not covered by lifecycle configuration”

    eclipse在其POM文件的一处提示出错如下: Plugin execution not covered by lifecycle configuration: org.apache.maven.p ...

  8. 〖Android〗OK6410a的Android HAL层代码编写笔记

    一.编写LED灯的Linux驱动程序代码 之所以使用存在HAL层,是为了保护对硬件驱动过程的逻辑与原理: 所以,残留在Linux驱动层的代码,只保留了基本的读写操作,而不含有关键的逻辑思维: 1. l ...

  9. map函数原理

    # -*- coding: utf-8 -*- #python 27 #xiaodeng #map函数 #map函数会对一个序列对象中的每一个元素应用被传入的函数,并返回一个包含了所有函数调用结果的一 ...

  10. 微信小程序:input输入框和form表单几种传值和取值方式

    1.传值:index下标传值.页面navigator传值 1.index下标 实现方式是:data-index="{{index}}"挖坑及e.currentTarget.data ...